using System;
using System.IO;
using System.Net;
using System.Linq;
using System.Custom;
using System.Drawing;
using System.Xml.Linq;
using System.Collections;
using CoreTypes.Properties;
using System.Custom.Window;
using System.Windows.Forms;
using System.Globalization;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;

namespace CoreTypes
{
   [DataContract(Namespace = "CoreTypes")]
   public class User
   {
      #region User Data

      protected internal string FNickname;
      protected internal string FIdentifier;
      protected internal string FCustomName;
      protected internal UserInfo FInfo = new UserInfo();

      #endregion

      [DataMember(IsRequired = false)]
      public UserInfo Info
      {
         get
         {
            return FInfo;
         }
         set
         {
            FInfo = value;
         }
      }

      [DataMember(IsRequired = true)]
      public string Nickname
      {
         get
         {
            return string.IsNullOrEmpty(FNickname) ?
                  (string.IsNullOrEmpty(FCustomName) ? FIdentifier : FCustomName) : FNickname;
         }
         set
         {
            FNickname = value;

            if (CustomNameIsBad(this)) FCustomName = value;
         }
      }

      [DataMember(IsRequired = true)]
      public string Identifier
      {
         get
         {
            return FIdentifier;
         }
         set
         {
            if (this is Contact || (this is Account && (this as Account).IsValid)) throw new Exception("The identifier of the contact or account cannot be changed directly.");

            if (string.IsNullOrEmpty(value)) throw new Exception("The Identifier of the user cannot be empty.");

            FIdentifier = value;
         }
      }

      public string Login
      {
         get
         {
            return FIdentifier.Contains(':') ? FIdentifier.SubstrAfter(":") : FIdentifier;
         }
      }
      public string Username
      {
         get
         {
            return GetUsername(Login);
         }
      }
      public string CustomName
      {
         get
         {
            return string.IsNullOrEmpty(FCustomName) ? Nickname : FCustomName;
         }
         set
         {
            FCustomName = value;
         }
      }
      public virtual string LoginPrefix
      {
         get
         {
            return FIdentifier.Contains(':') ? FIdentifier.SubstrBefore(":") : "";
         }
      }

      public User ToUser()
      {
         User user = new User(Identifier, Nickname, Info);
         user.CustomName = CustomName;
         return user;
      }
      public void CopyFrom(User source)
      {
         Nickname = source.Nickname;
         Info = source.Info;

         if (CustomName == Identifier) CustomName = Nickname;
      }
      public override string ToString()
      {
         return CustomName + " " + Identifier;
      }

      public User(string info)
      {
         if (info != null) info = info.TrimIgnoreNull();
         if (string.IsNullOrEmpty(info)) throw new Exception("Unable to create user object from empty info.");

         if (info.Contains(' '))
         {
            FCustomName = info.SubstrBeforeLast(" ");
            info = info.SubstrAfterLast(" ");
         }

         FIdentifier = info;
         FNickname = Username;
      }
      public User(string identifier, string nick)
      {
         if (string.IsNullOrEmpty(identifier)) throw new Exception("Identifier of the user cannot be empty.");

         FIdentifier = identifier;
         FNickname = string.IsNullOrEmpty(nick) ? Username : nick;
      }
      public User(string identifier, string nick, UserInfo info)
      {
         if (string.IsNullOrEmpty(identifier)) throw new Exception("Identifier of the user cannot be empty.");

         FInfo = info;
         FIdentifier = identifier;
         FNickname = string.IsNullOrEmpty(nick) ? Username : nick;
      }

      public static bool CustomNameIsBad(User user)
      {
         return user.CustomName == user.Login && user.Login == user.Username;
      }
      public static string GetUsername(string login)
      {
         return login.SubstrBeforeLast("@").ToLower();
      }
      public static string GetUserFilenameToken(User user)
      {
         return user.Identifier.Replace(':', ' ').Replace("\\20", " ");
      }
   }

   [DataContract(Namespace = "CoreTypes")]
   public class UserInfo
   {
      #region UserInfo Data

      Image FUserpic;
      string FFullName;
      string FEmail;
      string FPhone;
      string FCity;
      string FCountry;
      string FLanguage;
      string FPersonalUrl;
      string FDescription;

      WorkInfo FWork = new WorkInfo();

      static UserInfo FEmpty;

      #endregion

      [DataMember]
      public string FullName
      {
         get
         {
            return FFullName == null ? "" : FFullName;
         }
         set
         {
            FFullName = value;
         }
      }
      [DataMember]
      public string Email
      {
         get
         {
            return FEmail == null ? "" : FEmail;
         }
         set
         {
            FEmail = value;
         }
      }
      [DataMember]
      public string Phone
      {
         get
         {
            return FPhone == null ? "" : FPhone;
         }
         set
         {
            FPhone = value;
         }
      }
      [DataMember]
      public string City
      {
         get
         {
            return FCity == null ? "" : FCity;
         }
         set
         {
            FCity = value;
         }
      }
      [DataMember]
      public string Country
      {
         get
         {
            return FCountry == null ? "" : FCountry;
         }
         set
         {
            FCountry = value;
         }
      }
      [DataMember]
      public string Language
      {
         get
         {
            return FLanguage == null ? "" : FLanguage;
         }
         set
         {
            FLanguage = value;
         }
      }
      [DataMember]
      public string PersonalUrl
      {
         get
         {
            return FPersonalUrl == null ? "" : FPersonalUrl;
         }
         set
         {
            FPersonalUrl = value;
         }
      }
      [DataMember]
      public string Description
      {
         get
         {
            return FDescription == null ? "" : FDescription;
         }
         set
         {
            FDescription = value;
         }
      }
      [DataMember]
      public int? Gender { get; set; }
      [DataMember]
      public int? Height { get; set; }
      [DataMember]
      public int? Weight { get; set; }
      [DataMember]
      public string Birthday { get; set; }
      [DataMember]
      public int? FamilyStatus { get; set; }
      [DataMember]
      public WorkInfo Work
      {
         get
         {
            return FWork;
         }
      }

      public string Age
      {
         get
         {
            DateTime? date = GetBirthdayAsDate(false, true, true);

            int age = date == null ? -1 : (int)((DateTime.Now - date.Value).Days / 365);

            return age <= 0 || age > 100 ? "" : age.ToString();
         }
      }
      public Image Userpic
      {
         get
         {
            return HasUserpic ? new Bitmap(FUserpic) : null;
         }
         set
         {
            if (HasUserpic) FUserpic.Dispose();

            FUserpic = value;
         }
      }
      public bool HasUserpic
      {
         get
         {
            return FUserpic != null;
         }
      }
      public static UserInfo Empty
      {
         get
         {
            return FEmpty;
         }
      }

      public static bool IsValidEmail(string str)
      {
         if (string.IsNullOrEmpty(str) || str.Length < 3 || !str.Contains('@')) return false;

         return str.Contains('.') ? (str.IndexOf('.') > str.IndexOf('@')) : true;
      }
      public string GetBirthdayAsString(bool full)
      {
         DateTime? birthday = GetBirthdayAsDate(false, false, false);

         return birthday == null ? "" : (full ? birthday.Value.ToLongDateString() : birthday.Value.ToShortDateString());
      }
      public string GetWeightAsString(bool metric)
      {
         return Weight == null ? "" : Weight.ToString();
      }
      public string GetHeightAsString(bool metric)
      {
         return Height == null ? "" : Height.ToString();
      }
      public static string GetGenderAsString(int? gender, bool full)
      {
         switch (gender)
         {
            case 1: return full ? "Male" : "M";
            case 0: return full ? "Female" : "F";
         }
         return "";
      }
      public static string GetFamilyStatusAsString(int? familyStatus, bool full)
      {
         switch (familyStatus)
         {
            case 0:
            {
               switch (familyStatus)
               {
                  default: return full ? "Unmarried" : "U";
               }
            }
            case 1:
            {
               switch (familyStatus)
               {
                  default: return full ? "Married" : "M";
               }
            }
         }
         return "";
      }
      public DateTime? GetBirthdayAsDate(bool fakeYear, bool fakeMonth, bool fakeDay)
      {
         return GetBirthdayAsDate(Birthday, fakeYear, fakeMonth, fakeDay);
      }
      public static bool ParseBirthday(string birthday, out int year, out int month, out int day)
      {
         year = 0; month = 0; day = 0;

         if (string.IsNullOrEmpty(birthday)) return false;

         string[] dateAttr = birthday.Split('.', '/');
         if (dateAttr.Length != 3) return false;

         try { year = Convert.ToInt32(dateAttr[0]); }
         catch { return false; }

         if (year != 0 && (year < 1900 || year > 1994)) return false;

         try { month = Convert.ToInt32(dateAttr[1]); }
         catch { return false; }

         if (month != 0 && (month < 0 || month > 12)) return false;

         try { day = Convert.ToInt32(dateAttr[2]); }
         catch { return false; }

         if (day != 0 && (day < 0 || day > 31)) return false;

         if (month > 0 && day > 0)
         {
            try { DateTime date = new DateTime(year > 0 ? year : 4, month, day); }
            catch { return false; }
         }
         else if (month == 0 && day != 0)
         {
            return false;
         }

         return true;
      }
      public static DateTime? GetBirthdayAsDate(string birthday, bool fakeYear, bool fakeMonth, bool fakeDay)
      {
         DateTime dt;

         if (DateTime.TryParse(birthday, out dt))
         {
            return dt;
         }
         else
         {
            int year, month, day;

            if (ParseBirthday(birthday, out year, out month, out day))
            {
               try
               {
                  return new DateTime(year > 0 ? year : (fakeYear ? 4 : 0),
                                      month > 0 ? month : (fakeMonth ? 1 : 0),
                                      day > 0 ? day : (fakeDay ? 1 : 0));
               }
               catch
               {
                  return null;
               }
            }
            return null;
         }
      }

      static UserInfo()
      {
         FEmpty = new UserInfo();
      }
      public UserInfo()
      {
      }
      public UserInfo(string name, int? gender, string language, string country,
         string city, string birthday, string description)
      {
         FullName = name;
         City = city;
         Gender = gender;
         Country = country;
         Language = language;
         Birthday = birthday;
         Description = description;
      }
      public UserInfo(string name, string email, int? gender, string language,
         string country, string city, string birthday, string phone, string webAddress,
         string description, int? height, int? weight, int? familyStatus)
      {
         FullName = name;
         City = city;
         Email = email;
         Phone = phone;
         Gender = gender;
         Height = height;
         Weight = weight;
         Country = country;
         Language = language;
         Birthday = birthday;
         PersonalUrl = webAddress;
         Description = description;
         FamilyStatus = familyStatus;
      }
   }

   [DataContract(Namespace = "CoreTypes")]
   public class WorkInfo
   {
      #region UserInfo Data

      string FUrl;
      string FFax;
      string FRole;
      string FEmail;
      string FPhone;
      string FCity;
      string FCountry;
      string FAddress;
      string FDepartment;
      string FOrganization;

      static WorkInfo FEmpty;

      #endregion

      [DataMember]
      public string Email
      {
         get
         {
            return FEmail == null ? "" : FEmail;
         }
         set
         {
            FEmail = value;
         }
      }
      [DataMember]
      public string Phone
      {
         get
         {
            return FPhone == null ? "" : FPhone;
         }
         set
         {
            FPhone = value;
         }
      }
      [DataMember]
      public string City
      {
         get
         {
            return FCity == null ? "" : FCity;
         }
         set
         {
            FCity = value;
         }
      }
      [DataMember]
      public string Country
      {
         get
         {
            return FCountry == null ? "" : FCountry;
         }
         set
         {
            FCountry = value;
         }
      }
      [DataMember]
      public string Address
      {
         get
         {
            return FAddress == null ? "" : FAddress;
         }
         set
         {
            FAddress = value;
         }
      }
      [DataMember]
      public string Url
      {
         get
         {
            return FUrl == null ? "" : FUrl;
         }
         set
         {
            FUrl = value;
         }
      }
      [DataMember]
      public string Fax
      {
         get
         {
            return FFax == null ? "" : FFax;
         }
         set
         {
            FFax = value;
         }
      }
      [DataMember]
      public string Role
      {
         get
         {
            return FRole == null ? "" : FRole;
         }
         set
         {
            FRole = value;
         }
      }
      [DataMember]
      public string Department
      {
         get
         {
            return FDepartment == null ? "" : FDepartment;
         }
         set
         {
            FDepartment = value;
         }
      }
      [DataMember]
      public string Organization
      {
         get
         {
            return FOrganization == null ? "" : FOrganization;
         }
         set
         {
            FOrganization = value;
         }
      }

      public bool IsPresent
      {
         get
         {
            return !string.IsNullOrEmpty(FUrl) ||
                   !string.IsNullOrEmpty(FFax) ||
                   !string.IsNullOrEmpty(FRole) ||
                   !string.IsNullOrEmpty(FEmail) ||
                   !string.IsNullOrEmpty(FPhone) ||
                   !string.IsNullOrEmpty(FCity) ||
                   !string.IsNullOrEmpty(FCountry) ||
                   !string.IsNullOrEmpty(FAddress) ||
                   !string.IsNullOrEmpty(FDepartment) ||
                   !string.IsNullOrEmpty(FOrganization);
         }
      }

      public static WorkInfo Empty
      {
         get
         {
            return FEmpty;
         }
      }

      static WorkInfo()
      {
         FEmpty = new WorkInfo();
      }
      public WorkInfo()
      {
      }
   }

   [DataContract(Namespace = "CoreTypes")]
   public class Account : User
   {
      #region Account Data

      Status FStatus;
      XElement FXSettings;
      bool FRememberPassword;
      string FDefaultGroupName;
      ContactGroup FRootGroup;
      ContactGroup FDefaultGroup;
      ServiceConfig FServiceConfig;
      ContactList FContacts = new ContactList();
      AccountSettings FSettings = new AccountSettings();
      ContactsState FContactsState = ContactsState.Initial;
      List<ContactGroup> FGroups = new List<ContactGroup>();

      #endregion

      public bool IsValid
      {
         get
         {
            return Communicator.Accounts.Contains(this);
         }
      }
      public Status Status
      {
         get
         {
            return FStatus;
         }
         set
         {
            FStatus = value;
         }
      }
      public bool Connected
      {
         get
         {
            return ServiceDriver == null ? false : ServiceDriver.IsConnected(this);
         }
      }
      public bool AutoLogon
      { get; set; }
      public string Password
      { get; set; }
      public string LoginEmail
      { get; set; }
      public string ServiceName
      {
         get
         {
            return FServiceConfig.Service.ServiceName;
         }
      }
      public string AwayMessage
      { get; set; }
      public int ContactsVersion
      { get; set; }
      public string OnlineMessage
      { get; set; }
      public string StatusMessage
      {
         get
         {
            string msg = Status.IsAway() ? AwayMessage : OnlineMessage;

            return msg == null ? "" : msg;
         }
         set
         {
            if (Status.IsAway()) AwayMessage = value; else OnlineMessage = value;
         }
      }
      public bool RememberPassword
      {
         get
         {
            return FRememberPassword;
         }
         set
         {
            FRememberPassword = value;
            if (!FRememberPassword) Password = "";
         }
      }
      public override string LoginPrefix
      {
         get
         {
            return FServiceConfig == null ? base.LoginPrefix : FServiceConfig.Service.LoginPrefix;
         }
      }

      public XElement XObject
      {
         get
         {
            XElement xAccount = new XElement("Account");

            xAccount.SetAttributeValue("Login", Login);
            xAccount.SetAttributeValue("Transport", ServiceConfig.TransportString);
            if (Login != CustomName) xAccount.SetAttributeValue("CustomName", CustomName);
            if (!string.IsNullOrEmpty(LoginEmail)) xAccount.SetAttributeValue("LoginEmail", LoginEmail);
            if (!string.IsNullOrEmpty(OnlineMessage)) xAccount.SetAttributeValue("Greeting", OnlineMessage);
            if (!string.IsNullOrEmpty(AwayMessage)) xAccount.SetAttributeValue("AwayMessage", AwayMessage);

            if (XSettings.HasAttributes) xAccount.Add(XSettings);

            return xAccount;
         }
         set
         {
            XAttribute customName = value.Attribute("CustomName");
            XAttribute loginEmail = value.Attribute("LoginEmail");
            XAttribute greeting = value.Attribute("Greeting");
            XAttribute awayMessage = value.Attribute("AwayMessage");

            if (customName != null) CustomName = (string)customName;
            if (loginEmail != null) LoginEmail = (string)loginEmail;
            if (greeting != null) OnlineMessage = (string)greeting;
            if (awayMessage != null) AwayMessage = (string)awayMessage;

            XSettings = value.Element("Settings");
         }
      }
      public XElement XContacts
      {
         get
         {
            return GetXContacts();
         }
      }
      public XElement XSettings
      {
         get
         {
            if (FXSettings == null) FXSettings = new XElement("Settings"); else FXSettings.RemoveAttributes();

            if (FSettings.MyContactMessageRule != PolicyRule.None) FXSettings.SetAttributeValue("MyContactMessageRule", FSettings.MyContactMessageRule.ToString());
            if (FSettings.MyContactInvitationRule != PolicyRule.None) FXSettings.SetAttributeValue("MyContactInvitationRule", FSettings.MyContactInvitationRule.ToString());
            if (FSettings.VipContactInvitationRule != PolicyRule.None) FXSettings.SetAttributeValue("VipContactInvitationRule", FSettings.VipContactInvitationRule.ToString());
            if (FSettings.UnknownUserMessageRule != PolicyRule.None) FXSettings.SetAttributeValue("UnknownUserMessageRule", FSettings.UnknownUserMessageRule.ToString());
            if (FSettings.UnknownUserInvitationRule != PolicyRule.None) FXSettings.SetAttributeValue("UnknownUserInvitationRule", FSettings.UnknownUserInvitationRule.ToString());
            if (FSettings.StartupStatus != StatusCodes.Online) FXSettings.SetAttributeValue("StartupStatus", FSettings.StartupStatus.ToString());
            if (FSettings.AwayTimeout >= 0) FXSettings.SetAttributeValue("AwayTimeout", FSettings.AwayTimeout);
            if (FSettings.LogonDelay > 0) FXSettings.SetAttributeValue("LogonDelay", FSettings.LogonDelay);
            if (FSettings.NATimeout >= 0) FXSettings.SetAttributeValue("NATimeout", FSettings.NATimeout);

            return FXSettings;
         }
         set
         {
            if (value != null)
            {
               XAttribute myContactMessageRule = value.Attribute("MyContactMessageRule");
               XAttribute myContactInvitationRule = value.Attribute("MyContactInvitationRule");
               XAttribute vipContactMessageRule = value.Attribute("VipContactMessageRule");
               XAttribute vipContactInvitationRule = value.Attribute("VipContactInvitationRule");
               XAttribute unknownUserMessageRule = value.Attribute("UnknownUserMessageRule");
               XAttribute unknownUserInvitationRule = value.Attribute("UnknownUserInvitationRule");
               XAttribute startupStatus = value.Attribute("StartupStatus");
               XAttribute awayTimeout = value.Attribute("AwayTimeout");
               XAttribute logonDelay = value.Attribute("LogonDelay");
               XAttribute naTimeout = value.Attribute("NATimeout");

               try
               {
                  if (myContactMessageRule != null) FSettings.MyContactMessageRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)myContactMessageRule, true);
                  if (myContactInvitationRule != null) FSettings.MyContactInvitationRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)myContactInvitationRule, true);
                  if (vipContactInvitationRule != null) FSettings.VipContactInvitationRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)vipContactInvitationRule, true);
                  if (unknownUserMessageRule != null) FSettings.UnknownUserMessageRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)unknownUserMessageRule, true);
                  if (unknownUserInvitationRule != null) FSettings.UnknownUserInvitationRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)unknownUserInvitationRule, true);
                  if (startupStatus != null) FSettings.StartupStatus = (StatusCodes)Enum.Parse(typeof(StatusCodes), (string)startupStatus, true);
                  if (awayTimeout != null) FSettings.AwayTimeout = (int)awayTimeout;
                  if (logonDelay != null) FSettings.LogonDelay = (int)logonDelay;
                  if (naTimeout != null) FSettings.NATimeout = (int)naTimeout;
               }
               catch
               {
                  FXSettings = null; return;
               }
            }

            FXSettings = value;
         }
      }
      public Contact[] Contacts
      {
         get
         {
            return FContacts.ToArray();
         }
      }
      public ContactGroup[] Groups
      {
         get
         {
            return FGroups.ToArray();
         }
      }
      public ContactGroup RootGroup
      {
         get
         {
            return FRootGroup;
         }
      }
      public AccountSettings Settings
      {
         get
         {
            return FSettings;
         }
         set
         {
            FSettings = value;
         }
      }
      public ContactGroup DefaultGroup
      {
         get
         {
            return FDefaultGroup;
         }
         private set
         {
            FDefaultGroup = value;

            if (!FGroups.Contains(value) && value != FRootGroup) AddGroup(value);
         }
      }
      public ContactsState ContactsState
      {
         get
         {
            return FContactsState;
         }
         set
         {
            FContactsState = value;
         }
      }
      public ServiceConfig ServiceConfig
      {
         get
         {
            return FServiceConfig;
         }
         set
         {
            if (value == null) return;

            if (FServiceConfig.Service != value.Service) throw new InvalidOperationException("Changing user service is not allowed.");

            FServiceConfig = value;
         }
      }
      internal IBaseCommunicator Communicator
      {
         get
         {
            return FServiceConfig != null ? FServiceConfig.Service.Driver.Communicator : null;
         }
      }
      public IEnumerable<Contact> OnlineContacts
      {
         get
         {
            return FContacts.Where(contact => contact.Status.Value != StatusCodes.Offline);
         }
      }
      public IEnumerable<Contact> ActiveContacts
      {
         get
         {
            return FContacts.Where(contact => contact.Visible && !IsBlockedContact(contact.Identifier, PrivacyType.BlockVisibility));
         }
      }
      public CommunicationServiceObjectBase ServiceDriver
      {
         get
         {
            return FServiceConfig != null ? FServiceConfig.Service.Driver : null;
         }
      }

      internal void EvalDefaultGroup()
      {
         IEnumerable<ContactGroup> userGroups = FGroups.Where(g => !g.IsSystem);

         FDefaultGroup = string.IsNullOrEmpty(FDefaultGroupName) ? null : userGroups.FirstOrDefault(g =>
                         g.Name.ToLower().Contains(FDefaultGroupName.SubstrBefore(" ").ToLower()) ||
                         FDefaultGroupName.ToLower().Contains(g.Name.SubstrBefore(" ").ToLower()));

         if (FDefaultGroup == null)
         {
            if (userGroups.Count() == 0)
            {
               if (Connected)
               {
                  DefaultGroup = Communicator.AddContactGroup(Resources.S_General_contacts, null, this);
               }
            }
            else
            {
               // Select the group which contains more contacts
               if (userGroups.Count() > 1)
               {
                  FGroups.Sort((g1, g2) =>
                  {
                     return FContacts.Count(c => c.Groups.Contains(g2)) - FContacts.Count(c => c.Groups.Contains(g1));
                  });
               }
               FDefaultGroup = userGroups.ElementAt(0);
            }
         }
      }
      internal void AddGroup(ContactGroup group)
      {
         if (group.Owner != this) throw new InvalidOperationException("Cannot add group from another account.");

         FGroups.Add(group);
      }
      internal Contact AddContact(string id, string nick, string customName, ContactGroup group, bool myInitiative)
      {
         Contact contact = new Contact(this, id, nick, customName, group);
         if (group == null) contact.Group = DefaultGroup;
         contact.IsUnknownPerson = !myInitiative;
         FContacts.Add(contact);
         return contact;
      }

      public XElement GetXContacts()
      {
         if (FContacts.Count > 0 || FGroups.Count > 0)
         {
            XElement xContacts = new XElement("Contacts");
            xContacts.SetAttributeValue("Owner", Identifier);
            xContacts.SetAttributeValue("Version", ContactsVersion);
            if (FDefaultGroup != null) xContacts.SetAttributeValue("DefaultGroup", FDefaultGroup.Name);

            // Adding root user defined groups
            foreach (ContactGroup group in FGroups.Where(g => !g.IsSystem && string.IsNullOrEmpty(g.ParentName)))
            {
               xContacts.Add(group.XObject);
            }
            // Adding second level - bad way, but it's hard to imagine 3 or more level contact list hierarchy
            foreach (ContactGroup group in FGroups.Where(g => !g.IsSystem && !string.IsNullOrEmpty(g.ParentName)))
            {
               XElement xParent = xContacts.Descendants("Group").FirstOrDefault(x => (string)x.Attribute("Name") == group.ParentName);
               if (xParent != null) xParent.Add(group.XObject); else xContacts.Add(group.XObject);
            }

            foreach (Contact contact in FContacts) xContacts.Add(contact.XObject);

            return xContacts;
         }
         else
         {
            return null;
         }
      }
      public void ClearLocalContacts()
      {
         foreach (Contact c in FContacts.ToArray()) if (c.IsLocal) FContacts.Remove(c);
      }
      public bool IsContact(User user)
      {
         return (user is Contact) && FContacts.Contains((Contact)user);
      }
      public bool IsContact(string identifier)
      {
         return FContacts.Contains(identifier);
      }
      public void BlockContact(Contact contact)
      {
         ContactSettings settings = contact.Settings;

         settings.MessageRule = PolicyRule.Block;
         settings.InvitationRule = PolicyRule.Block;
         settings.VisibilityRule = PolicyRule.Block;

         contact.Settings = settings;
      }
      public void RemoveGroup(ContactGroup group)
      {
         foreach (Contact c in FContacts) c.Groups.Remove(group);

         FGroups.Remove(group);
      }
      public bool IsVipContact(string identifier)
      {
         Contact contact = GetContact(identifier);

         if (contact == null) return false;

         ContactSettings settings = contact.Settings;

         return settings.MessageRule == PolicyRule.Allow ||
                settings.InvitationRule == PolicyRule.Allow ||
                settings.VisibilityRule == PolicyRule.Allow;
      }
      public Contact GetContact(string identifier)
      {
         return FContacts.GetItemById(identifier);
      }
      public bool IsUnknownContact(string identifier)
      {
         Contact contact = GetContact(identifier);

         return contact == null || contact.IsUnknownPerson;
      }
      public void SetXContacts(XElement xml, bool remote)
      {
         IEnumerable<XElement> xGroups = xml.Descendants("Group");
         IEnumerable<XElement> xContacts = xml.Descendants("Contact");

         XAttribute xVersion = xml.Attribute("Version");
         XAttribute xDefaultGroup = xml.Attribute("DefaultGroup");

         if (xVersion != null) ContactsVersion = (int)xVersion;
         if (xDefaultGroup != null) FDefaultGroupName = (string)xDefaultGroup;

         foreach (XElement xElement in xGroups)
         {
            if (FGroups.Count(g => g.Name == (string)xElement.Attribute("Name")) == 0)
            {
               FGroups.Add(new ContactGroup(this, xElement, 0, false));
            }
         }

         EvalDefaultGroup(); // by name or any group

         List<Contact> remoteContacts = null;

         if (remote) remoteContacts = new List<Contact>();

         foreach (XElement xElement in xContacts)
         {
            string id = LoginPrefix + ":" + (string)xElement.Attribute("Login");

            Contact contact = FContacts.GetItemById(id);

            if (contact == null)
            {
               FContacts.Add(contact = new Contact(this, id, xElement));
            }
            else
            {
               contact.XObject = xElement;
            }
            if (remote) remoteContacts.Add(contact);
         }

         if (remote) foreach (Contact c in FContacts.Except(remoteContacts)) c.IsLocal = true;

         EvalDefaultGroup(); // by name or by user count
      }
      public void RemoveContact(Contact contact, bool block)
      {
         if (!IsContact(contact)) return;

         FContacts.Remove(contact);

         if (block) BlockContact(contact);

         foreach (IMediaSession session in contact.MediaSessions.ToArray()) session.RemoveParticipant(contact);

         contact.Owner = null;
      }
      public void MoveContact(Contact contact, ContactGroup group)
      {
         if (!IsContact(contact)) return;

         if (contact.Groups.Count > 1) throw new InvalidOperationException(Resources.S_This_contact_exist_in_more_than_one_group);

         contact.Group = group;
      }
      public bool IsBlockedContact(string identifier, PrivacyType type)
      {
         Contact contact = GetContact(identifier);

         if (contact == null) return false;

         ContactSettings settings = contact.Settings;

         int blocked = 0;

         if (type == PrivacyType.All || (type & PrivacyType.BlockInvitation) == PrivacyType.BlockInvitation)
         {
            if (settings.InvitationRule == PolicyRule.Deny || settings.InvitationRule == PolicyRule.Block) blocked++;
         }

         if (type == PrivacyType.All || (type & PrivacyType.BlockMessage) == PrivacyType.BlockMessage)
         {
            if (settings.MessageRule == PolicyRule.Deny || settings.MessageRule == PolicyRule.Block) blocked++;
         }

         if (type == PrivacyType.All || (type & PrivacyType.BlockVisibility) == PrivacyType.BlockVisibility)
         {
            if (settings.VisibilityRule == PolicyRule.Deny || settings.VisibilityRule == PolicyRule.Block) blocked++;
         }

         return blocked > 0;
      }

      public string ToString(InfoStrings what)
      {
         switch (what)
         {
            case InfoStrings.EmailLike: return CustomName + "@" + ServiceConfig.ToString().Replace(' ', '_');
            case InfoStrings.NameStatus: return CustomName + " " + Status.Sign;
            case InfoStrings.NameService: return CustomName + Resources.S_s + " " + ServiceConfig.ToString();
            case InfoStrings.ServiceLogin: return ServiceConfig.ToString() + ": " + Login;
            case InfoStrings.NameServiceLogin: return CustomName + Resources.S_s + " " + ServiceConfig.ToString() + ": " + Login;
            case InfoStrings.NameServiceStatus: return CustomName + Resources.S_s + " " + ServiceConfig.ToString() + " - " + Status.Sign;
            case InfoStrings.NameServiceFullStatus: return CustomName + Resources.S_s + " " + ServiceConfig.ToString() + " - " + Status.ToLongString();
            default: return CustomName;
         }
      }

      public Account(string identifier, string nick, ServiceConfig config)
         : base(identifier, nick)
      {
         if (config == null) throw new Exception(Resources.S_Account_can_not_be_created_with_empty_configuration);

         AutoLogon = true;
         RememberPassword = true;
         FServiceConfig = config;
         Status = Status.AutoOffline;
         FRootGroup = new ContactGroup(Resources.S_All_contacts, null, this, true, 0, true);
         DefaultGroup = RootGroup;  // Fake, but better then null

         FSettings.NATimeout = -1;
         FSettings.LogonDelay = 0;
         FSettings.AwayTimeout = -1;
         FSettings.StartupStatus = StatusCodes.Online;
         FSettings.MyContactMessageRule = PolicyRule.None;
         FSettings.MyContactInvitationRule = PolicyRule.None;
         FSettings.VipContactInvitationRule = PolicyRule.None;
         FSettings.UnknownUserMessageRule = PolicyRule.None;
         FSettings.UnknownUserInvitationRule = PolicyRule.None;
      }

      public Account(string identifier, XElement xObject, ServiceConfig config)
         : base(identifier, (string)xObject.Attribute("Nickname"))
      {
         AutoLogon = true;
         RememberPassword = true;
         FServiceConfig = config;
         Status = Status.AutoOffline;
         FRootGroup = new ContactGroup(Resources.S_All_contacts, null, this, true, 0, true);
         DefaultGroup = RootGroup;  // Fake, but better then null

         FSettings.NATimeout = -1;
         FSettings.LogonDelay = 0;
         FSettings.AwayTimeout = -1;
         FSettings.StartupStatus = StatusCodes.Online;
         FSettings.MyContactMessageRule = PolicyRule.None;
         FSettings.MyContactInvitationRule = PolicyRule.None;
         FSettings.VipContactInvitationRule = PolicyRule.None;
         FSettings.UnknownUserMessageRule = PolicyRule.None;
         FSettings.UnknownUserInvitationRule = PolicyRule.None;

         XObject = xObject;
      }
   }

   [DataContract(Namespace = "CoreTypes")]
   public class Contact : User
   {
      #region Contact Data

      Status FStatus;
      Account FOwner;
      XElement FXSettings;
      ContactSettings FSettings;
      ServiceSession FServiceSession;
      MediaSessionSet FMediaSessions;
      Collection<ContactGroup> FGroups;

      #endregion

      public bool Visible
      { get; internal set; }
      public bool IsLocal
      { get; internal set; }
      public bool SignedOn
      { get; private set; }
      public bool SignedOff
      { get; private set; }
      public ushort PeerPort
      { get; set; }
      public string PeerAddr
      { get; set; }
      public string ServiceName
      {
         get
         {
            return FOwner.ServiceName;
         }
      }
      public bool IsUnknownPerson
      { get; internal set; }

      public Account Owner
      {
         get
         {
            return FOwner;
         }
         internal set
         {
            FOwner = value;
         }
      }
      public Status Status
      {
         get
         {
            return FStatus;
         }
         set
         {
            SignedOn = FStatus == Status.Offline && value != Status.Offline;
            SignedOff = FStatus != Status.Offline && value == Status.Offline;
            FStatus = value;
         }
      }
      public XElement XObject
      {
         get
         {
            XElement xContact = new XElement("Contact");

            xContact.SetAttributeValue("Login", Login);
            if (!Visible) xContact.SetAttributeValue("Visible", 0);
            if (IsUnknownPerson) xContact.SetAttributeValue("IsUnknownPerson", 1);
            if (Nickname == CustomName) xContact.SetAttributeValue("Nickname", Nickname);
            else xContact.SetAttributeValue("CustomName", CustomName);

            if (FGroups.Count > 0)
            {
               xContact.SetAttributeValue("Groups", "{" + string.Join("} {", FGroups.Select(g => g.Name).ToArray()) + "}");
            }

            if (XSettings.HasAttributes) xContact.Add(XSettings);

            return xContact;
         }
         set
         {
            XAttribute visible = value.Attribute("Visible");
            XAttribute nickname = value.Attribute("Nickname");
            XAttribute customName = value.Attribute("CustomName");
            XAttribute unknown = value.Attribute("IsUnknownPerson");
            XAttribute groups = value.Attribute("Groups");

            if (nickname != null) Nickname = (string)nickname;
            if (customName != null) CustomName = (string)customName;
            if (visible != null) Visible = visible.ToString().Length > 1 ? (bool)visible : ((int)visible == 0 ? false : true);
            if (unknown != null) IsUnknownPerson = unknown.ToString().Length > 1 ? (bool)unknown : ((int)unknown == 0 ? false : true);

            if (value.Parent.Name == "Group")
            {
               Group = FOwner.Groups.FirstOrDefault(g => g.Name == (string)value.Parent.Attribute("Name"));
            }
            else if (groups != null)
            {
               string[] groupNames = ((string)groups).Trim('{', '}').Split(new string[] { "|", "} {", "}{", "},{", "}, {" }, StringSplitOptions.RemoveEmptyEntries);

               FGroups.Clear();

               foreach (string groupName in groupNames)
               {
                  ContactGroup group = FOwner.Groups.FirstOrDefault(g => g.Name == groupName);

                  if (group != null && !FGroups.Contains(group)) FGroups.Add(group);
               }
            }
            if (FGroups.Count == 0) FGroups.Add(FOwner.DefaultGroup);

            XSettings = value.Element("Settings");
         }
      }
      public XElement XSettings
      {
         get
         {
            if (FXSettings == null) FXSettings = new XElement("Settings"); else FXSettings.RemoveAttributes();

            FXSettings.SetAttributeValue("NewWindow", FSettings.NewWindow);
            FXSettings.SetAttributeValue("NameColor", FSettings.NameColor);
            FXSettings.SetAttributeValue("TextColor", FSettings.TextColor);

            if (FSettings.MessageRule != PolicyRule.None) FXSettings.SetAttributeValue("MessageRule", FSettings.MessageRule);
            if (FSettings.InvitationRule != PolicyRule.None) FXSettings.SetAttributeValue("InvitationRule", FSettings.InvitationRule);
            if (FSettings.VisibilityRule != PolicyRule.None) FXSettings.SetAttributeValue("VisibilityRule", FSettings.VisibilityRule);

            return FXSettings;
         }
         set
         {
            if (value != null)
            {
               XAttribute newWindow = value.Attribute("NewWindow");
               XAttribute nameColor = value.Attribute("NameColor");
               XAttribute textColor = value.Attribute("TextColor");

               XAttribute messageRule = value.Attribute("MessageRule");
               XAttribute invitationRule = value.Attribute("InvitationRule");
               XAttribute visibilityRule = value.Attribute("VisibilityRule");

               try
               {
                  if (newWindow != null) FSettings.NewWindow = (bool?)newWindow;
                  if (nameColor != null) FSettings.NameColor = Color.FromName((string)nameColor);
                  if (textColor != null) FSettings.TextColor = Color.FromName((string)textColor);

                  if (messageRule != null) FSettings.MessageRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)messageRule, true);
                  if (invitationRule != null) FSettings.InvitationRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)invitationRule, true);
                  if (visibilityRule != null) FSettings.VisibilityRule = (PolicyRule)Enum.Parse(typeof(PolicyRule), (string)visibilityRule, true);
               }
               catch
               {
                  FXSettings = null;
                  return;
               }
            }

            FXSettings = value;
         }
      }
      public ContactGroup Group
      {
         get
         {
            return FGroups.Count == 0 ? null : FGroups[0];
         }
         internal set
         {
            FGroups.Clear();
            
            if (value != null) FGroups.Add(value);
         }
      }
      public ContactSettings Settings
      {
         get
         {
            return FSettings;
         }
         set
         {
            FSettings = value;
         }
      }
      public ServiceConfig ServiceConfig
      {
         get
         {
            return FOwner == null ? null : FOwner.ServiceConfig;
         }
      }
      public ServiceSession ServiceSession
      {
         get
         {
            return FServiceSession;
         }
         set
         {
            FServiceSession = value;
         }
      }
      public MediaSessionSet MediaSessions
      {
         get
         {
            if (FMediaSessions == null) FMediaSessions = new MediaSessionSet(true);

            return FMediaSessions;
         }
      }
      public Collection<ContactGroup> Groups
      {
         get
         {
            return FGroups;
         }
      }
      public ICommunicationServiceObject ServiceDriver
      {
         get
         {
            return FOwner == null ? null : FOwner.ServiceDriver;
         }
      }

      public string ToString(InfoStrings what)
      {
         switch (what)
         {
            case InfoStrings.NameStatus: return CustomName + " " + Status.Sign;
            case InfoStrings.NameService: return CustomName + Resources.S_s + " " + Owner.ServiceConfig.ToString();
            case InfoStrings.ServiceLogin: return Owner.ServiceConfig.ToString() + ": " + Login;
            case InfoStrings.NameServiceLogin: return CustomName + Resources.S_s + " " + Owner.ServiceConfig.ToString() + ": " + Login;
            case InfoStrings.NameServiceStatus: return CustomName + Resources.S_s + " " + Owner.ServiceConfig.ToString() + " - " + Status.Sign;
            case InfoStrings.NameServiceFullStatus: return CustomName + Resources.S_s + " " + Owner.ServiceConfig.ToString() + " - " + Status.ToLongString();
            default: return CustomName;
         }
      }

      public Contact(Account owner, string id, XElement xObject)
         : base(id, (string)xObject.Attribute("Nickname"))
      {
         Owner = owner;
         Status = Status.Offline;
         FSettings = new ContactSettings();
         FGroups = new Collection<ContactGroup>();
         //CustomName = (string)xObject.Attribute("CustomName");

         Visible = true;
         FSettings.MessageRule = PolicyRule.None;
         FSettings.InvitationRule = PolicyRule.None;
         FSettings.VisibilityRule = PolicyRule.None;

         XObject = xObject;
      }

      internal Contact(Account owner, string id, string nick, string customName, ContactGroup group)
         : base(id, nick)
      {
         FOwner = owner;
         Status = Status.Offline;
         CustomName = customName;
         FSettings = new ContactSettings();
         FGroups = new Collection<ContactGroup>();

         Group = group;
         Visible = true;
         FSettings.MessageRule = PolicyRule.None;
         FSettings.InvitationRule = PolicyRule.None;
         FSettings.VisibilityRule = PolicyRule.None;
      }
   }

   [DataContract(Namespace = "CoreTypes")]
   public class ContactGroup
   {
      public string Name
      {
         get;
         internal set;
      }
      public int SortTag
      {
         get;
         private set;
      }
      public bool IsSystem
      {
         get;
         private set;
      }
      public Account Owner
      {
         get;
         private set;
      }
      public bool Expanded
      {
         get;
         set;
      }
      public XElement XObject
      {
         get
         {
            XElement xGroup = new XElement("Group");

            xGroup.SetAttributeValue("Name", Name);
            if (Expanded) xGroup.SetAttributeValue("Expanded", true);

            return xGroup;
         }
         set
         {
            XAttribute xName = value.Attribute("Name");
            XAttribute xExpanded = value.Attribute("Expanded");

            Name = (string)xName;
            Expanded = xExpanded == null ? false : (bool)xExpanded;

            if (value.Parent.Name == "Group") ParentName = (string)value.Parent.Attribute("Name");
         }
      }
      public string ParentName
      {
         get;
         private set;
      }

      public ContactGroup(Account owner, XElement xElement, int sortTag, bool isSystem)
      {
         Owner = owner;
         SortTag = sortTag;
         IsSystem = isSystem;
         XObject = xElement;
      }
      public ContactGroup(string name, string parent, Account owner, bool expanded, int sortTag, bool isSystem)
      {
         Name = name;
         Owner = owner;
         SortTag = sortTag;
         ParentName = parent;
         Expanded = expanded;
         IsSystem = isSystem;
      }

      public override string ToString()
      {
         return Name;
      }
   }

   [DataContract(Namespace = "CoreTypes")]
   public struct Status
   {
      #region Status Data

      Image FImage;
      bool FAutoTrack;
      string FMessage;
      StatusCodes FValue;

      static Status FOnline;
      static Status FOffline;
      static Status FPhantom;
      static Status FInvisible;
      static Status FSeemsAway;
      static Status FNotAvailable;

      static Status FAutoAway;
      static Status FAutoOffline;
      static Status FAutoNotAvailable;

      #endregion

      static Status()
      {
         FOnline = new Status(StatusCodes.Online);
         FOffline = new Status(StatusCodes.Offline);
         FPhantom = new Status(StatusCodes.Phantom);
         FInvisible = new Status(StatusCodes.Invisible);
         FSeemsAway = new Status(StatusCodes.SeemsAway);
         FNotAvailable = new Status(StatusCodes.NotAvailable);

         FAutoAway = new Status(StatusCodes.SeemsAway, true);
         FAutoOffline = new Status(StatusCodes.Offline, true);
         FAutoNotAvailable = new Status(StatusCodes.NotAvailable, true);
      }
      public Status(StatusCodes value)
      {
         FMessage = "";
         FValue = value;
         FAutoTrack = false;
         FImage = GetImage(value);
      }
      public Status(StatusCodes value, bool autoTrack)
      {
         FMessage = "";
         FValue = value;
         FAutoTrack = autoTrack;
         FImage = GetImage(value);
      }
      public Status(StatusCodes value, string message)
      {
         FValue = value;
         FAutoTrack = false;
         FImage = GetImage(value);
         FMessage = TextSvc.RemoveHtml(message);
      }

      public string Sign
      {
         get
         {
            switch (FValue)
            {
               case StatusCodes.Online:
               {
                  return Resources.S_Online;
               }
               case StatusCodes.Offline:
               {
                  return Resources.S_Offline;
               }
               case StatusCodes.Phantom:
               {
                  return Resources.S_Phantom;
               }
               case StatusCodes.Invisible:
               {
                  return Resources.S_Invisible;
               }
               case StatusCodes.SeemsAway:
               {
                  return Resources.S_Away;
               }
               case StatusCodes.NotAvailable:
               {
                  return Resources.S_Not_available;
               }
               default:
               {
                  return FValue.ToString();
               }
            }
         }
      }
      public Image Image
      {
         get
         {
            return FImage;
         }
         set
         {
            FImage = value;
         }
      }
      public bool AutoTrack
      {
         get
         {
            return FAutoTrack;
         }
         set
         {
            FAutoTrack = value;
         }
      }
      [DataMember]
      public string Message
      {
         get
         {
            if (FMessage == null) FMessage = "";

            return FMessage.Replace("\n", " ").Replace("\r", "");
         }
         set
         {
            FMessage = value;
         }
      }
      [DataMember]
      public StatusCodes Value
      {
         get
         {
            return FValue;
         }
         set
         {
            FValue = value;
         }
      }

      public static Status Online
      {
         get
         {
            return FOnline;
         }
      }
      public static Status Offline
      {
         get
         {
            return FOffline;
         }
      }
      public static Status Phantom
      {
         get
         {
            return FPhantom;
         }
      }
      public static Status Invisible
      {
         get
         {
            return FInvisible;
         }
      }
      public static Status SeemsAway
      {
         get
         {
            return FSeemsAway;
         }
      }
      public static Status NotAvailable
      {
         get
         {
            return FNotAvailable;
         }
      }

      public static Status AutoAway
      {
         get
         {
            return FAutoAway;
         }
      }
      public static Status AutoOffline
      {
         get
         {
            return FAutoOffline;
         }
      }
      public static Status AutoNotAvailable
      {
         get
         {
            return FAutoNotAvailable;
         }
      }

      public bool IsAway()
      {
         return Value == StatusCodes.SeemsAway || Value == StatusCodes.NotAvailable;
      }
      public string ToLongString()
      {
         string text = Message;
         if (text != "" && !text.Contains(Sign)) text += " (" + Sign + ")";
         else if (text == "") text = Sign;
         return text;
      }
      public string ToShortString()
      {
         string text = Message;

         return text != "" ? text : (IsAway() ? Sign : "");
      }
      public static void SetupImages()
      {
         Bitmap offline = ResourceWrapper.GetImage("Status" + StatusCodes.Offline.ToString());
         Bitmap online = ResourceWrapper.GetImage("Status" + StatusCodes.Online.ToString());
         Bitmap phantom = ResourceWrapper.GetImage("Status" + StatusCodes.Phantom.ToString());
         Bitmap invisible = ResourceWrapper.GetImage("Status" + StatusCodes.Invisible.ToString());
         Bitmap seemsAway = ResourceWrapper.GetImage("Status" + StatusCodes.SeemsAway.ToString());
         Bitmap notAvailable = ResourceWrapper.GetImage("Status" + StatusCodes.NotAvailable.ToString());

         FOnline.Image = online;
         FOffline.Image = offline;
         FPhantom.Image = phantom;
         FInvisible.Image = invisible;
         FSeemsAway.Image = seemsAway;
         FNotAvailable.Image = notAvailable;
      }
      public override int GetHashCode()
      {
         return base.GetHashCode();
      }
      public override bool Equals(object obj)
      {
         return obj is Status ? ((Status)obj).Value == Value : false;
      }
      public static bool IsAway(StatusCodes value)
      {
         return value == StatusCodes.SeemsAway || value == StatusCodes.NotAvailable;
      }
      public static Image GetImage(StatusCodes value)
      {
         switch (value)
         {
            case StatusCodes.Online: return Online.Image;
            case StatusCodes.Offline: return Offline.Image;
            case StatusCodes.Phantom: return Phantom.Image;
            case StatusCodes.Invisible: return Invisible.Image;
            case StatusCodes.SeemsAway: return SeemsAway.Image;
            case StatusCodes.NotAvailable: return NotAvailable.Image;
            default: return null;
         }
      }
      public static bool operator ==(Status a, Status b)
      {
         return a.Value == b.Value;
      }
      public static bool operator !=(Status a, Status b)
      {
         return a.Value != b.Value;
      }
   }

   public class ServiceInfo
   {
      string FCulture = "";
      string FHomePage = "";
      string FLoginAlias = "";
      string FLoginSample = "";
      string FLoginPrefix = "";
      string FLoginCaption = "";
      string FRegisterUrl = "";
      string FPasswordUrl = "";
      string FPersonalUrl = "";
      string FServiceName = "";
      string FDescription = "";
      string FProtocolName = "";
      string FProtocolVersion = "";

      CommunicationServiceObjectBase FDriver;
      Dictionary<string, ServiceConfig> FConfigurations = new Dictionary<string, ServiceConfig>();

      public ServiceInfo(ServiceInfo original)
      {
         FHomePage = original.HomePage;
         FServiceName = original.ServiceName;
         FDescription = original.Description;
         FRegisterUrl = original.RegisterUrl;
         FPasswordUrl = original.PasswordUrl;
         FPersonalUrl = original.PersonalUrl;
         FLoginPrefix = original.LoginPrefix;
         FLoginCaption = original.LoginCaption;
         FProtocolName = original.ProtocolName;
         FProtocolVersion = original.ProtocolVersion;

         FDriver = original.Driver;
      }
      public ServiceInfo(CommunicationServiceObjectBase driver, XElement xInfo)
      {
         FDriver = driver;
         XObject = xInfo;
      }

      public string Culture
      {
         get
         {
            return FCulture.ToLower();
         }
         set
         {
            FCulture = value;
         }
      }
      public string HomePage
      {
         get
         {
            return FHomePage;
         }
         set
         {
            FHomePage = value;
         }
      }
      public string LoginAlias
      {
         get
         {
            return string.IsNullOrEmpty(FLoginAlias) ? LoginPrefix : FLoginAlias;
         }
         set
         {
            FLoginAlias = value;
         }
      }
      public string LoginSample
      {
         get
         {
            return FLoginSample;
         }
         set
         {
            FLoginSample = value;
         }
      }
      public string LoginPrefix
      {
         get
         {
            return string.IsNullOrEmpty(FLoginPrefix) ? ServiceName : FLoginPrefix;
         }
         set
         {
            FLoginPrefix = value;
         }
      }
      public string LoginCaption
      {
         get
         {
            return string.IsNullOrEmpty(FLoginCaption) ? LoginAlias : FLoginCaption;
         }
         set
         {
            FLoginCaption = value;
         }
      }
      public string ServiceName
      {
         get
         {
            if (FServiceName == null) FServiceName = "";

            return FServiceName;
         }
         set
         {
            FServiceName = value;
         }
      }
      public string Description
      {
         get
         {
            return FDescription;
         }
         set
         {
            FDescription = value;
         }
      }
      public string RegisterUrl
      {
         get
         {
            return FRegisterUrl;
         }
         set
         {
            FRegisterUrl = value;
         }
      }
      public string PasswordUrl
      {
         get
         {
            return FPasswordUrl;
         }
         set
         {
            FPasswordUrl = value;
         }
      }
      public string PersonalUrl
      {
         get
         {
            return FPersonalUrl;
         }
         set
         {
            FPersonalUrl = value;
         }
      }
      public string ProtocolName
      {
         get
         {
            return FProtocolName;
         }
         set
         {
            FProtocolName = value;
         }
      }
      public string ProtocolVersion
      {
         get
         {
            return FProtocolVersion;
         }
         set
         {
            FProtocolVersion = value;
         }
      }

      public virtual XElement XObject
      {
         get
         {
            XElement x = new XElement("Service");

            x.SetAttributeValue("Name", FServiceName);
            if (!string.IsNullOrEmpty(FHomePage)) x.SetAttributeValue("HomePage", FHomePage);
            if (!string.IsNullOrEmpty(FLoginAlias)) x.SetAttributeValue("LoginAlias", FLoginAlias);
            if (!string.IsNullOrEmpty(FLoginSample)) x.SetAttributeValue("LoginSample", FLoginSample);
            if (!string.IsNullOrEmpty(FLoginPrefix)) x.SetAttributeValue("LoginPrefix", FLoginPrefix);
            if (!string.IsNullOrEmpty(FLoginCaption)) x.SetAttributeValue("LoginCaption", FLoginCaption);
            if (!string.IsNullOrEmpty(FDescription)) x.SetAttributeValue("Description", FDescription);
            if (!string.IsNullOrEmpty(FRegisterUrl)) x.SetAttributeValue("RegisterUrl", FRegisterUrl);
            if (!string.IsNullOrEmpty(FPasswordUrl)) x.SetAttributeValue("PasswordUrl", FPasswordUrl);
            if (!string.IsNullOrEmpty(FPersonalUrl)) x.SetAttributeValue("PersonalUrl", FPersonalUrl);
            if (!string.IsNullOrEmpty(FProtocolName)) x.SetAttributeValue("ProtocolName", FProtocolName);
            if (!string.IsNullOrEmpty(FProtocolVersion)) x.SetAttributeValue("ProtocolVersion", FProtocolVersion);
            if (!string.IsNullOrEmpty(FCulture)) x.SetAttributeValue("Culture", FCulture);

            foreach (var config in FConfigurations) x.Add(config.Value.XObject);

            return x;
         }
         set
         {
            if (value == null) return;

            XAttribute name = value.Attribute("Name");
            XAttribute culture = value.Attribute("Culture");
            XAttribute desc = value.Attribute("Description");
            XAttribute homePage = value.Attribute("HomePage");
            XAttribute loginAlias = value.Attribute("LoginAlias");
            XAttribute loginSample = value.Attribute("LoginSample");
            XAttribute loginPrefix = value.Attribute("LoginPrefix");
            XAttribute loginCaption = value.Attribute("LoginCaption");
            XAttribute registerUrl = value.Attribute("RegisterUrl");
            XAttribute passwordUrl = value.Attribute("PasswordUrl");
            XAttribute personalUrl = value.Attribute("PersonalUrl");
            XAttribute protocolName = value.Attribute("ProtocolName");
            XAttribute protocolVersion = value.Attribute("ProtocolVersion");
            XAttribute defaultNamespace = value.Attribute("DefaultNamespace");

            if (name != null) FServiceName = (string)name;
            if (desc != null) FDescription = (string)desc;
            if (homePage != null) FHomePage = (string)homePage;
            if (culture != null) FCulture = (string)culture;
            if (loginAlias != null) FLoginAlias = (string)loginAlias;
            if (loginSample != null) FLoginSample = (string)loginSample;
            if (loginPrefix != null) FLoginPrefix = (string)loginPrefix;
            if (loginCaption != null) FLoginCaption = (string)loginCaption;
            if (registerUrl != null) FRegisterUrl = (string)registerUrl;
            if (passwordUrl != null) FPasswordUrl = (string)passwordUrl;
            if (personalUrl != null) FPersonalUrl = (string)personalUrl;
            if (protocolName != null) FProtocolName = (string)protocolName;
            if (protocolVersion != null) FProtocolVersion = (string)protocolVersion;
         }
      }
      public ServiceConfig[] Configurations
      {
         get
         {
            return FConfigurations.Values.ToArray();
         }
      }
      public CommunicationServiceObjectBase Driver
      {
         get
         {
            return FDriver;
         }
         internal set
         {
            FDriver = value;
         }
      }
      public virtual XElement GetXLiteObject(IEnumerable<ServiceConfig> configs)
      {
         XElement x = new XElement("Service");

         x.SetAttributeValue("Name", FServiceName);

         foreach (var config in configs) x.Add(config.XObject);

         return x;
      }

      public override string ToString()
      {
         return ServiceName;
      }
      public void GoVendorPage(VendorPage page)
      {
         switch (page)
         {
            case VendorPage.RegisterUser: Shell.OpenLink(RegisterUrl, "");
            break;
            case VendorPage.PersonalInfo: Shell.OpenLink(PersonalUrl, "");
            break;
            case VendorPage.RestorePassword: Shell.OpenLink(PasswordUrl, "");
            break;
            default: Shell.OpenLink(HomePage, "");
            break;
         }
      }
      internal void AddConfig(ServiceConfig config)
      {
         FConfigurations[config.Name] = config;
      }
      internal void RemoveConfig(ServiceConfig config)
      {
         FConfigurations.Remove(config.Name);
      }
   }

   public class ServiceConfig
   {
      protected string FName;
      protected string FHost;
      protected ushort FPort;
      protected bool FReadOnly;
      protected bool FViaGateway;
      protected ServiceInfo FService;
      protected bool FUserDefined = true;
      protected Dictionary<string, string> FCapabilities;

      protected virtual void SetDefaultCapabilities()
      {
         if (FCapabilities == null) FCapabilities = new Dictionary<string, string>(); else FCapabilities.Clear();

         FCapabilities.Add("GettingOf", StringExt.Join(new ServiceFields[]
         {
            ServiceFields.Id,

         }.Select(f => f.ToString()), " "));
      }

      public ServiceConfig(ServiceInfo info)
      {
         FService = info;
      }
      public ServiceConfig(XElement xConfig, ServiceInfo info)
      {
         FService = info;
         XObject = xConfig;
      }
      public ServiceConfig(ServiceConfig origin, bool userDefined)
      {
         FPort = origin.Port;
         FHost = origin.Host;
         FService = origin.Service;
         FUserDefined = userDefined;
         FViaGateway = origin.ViaGateway;
         FName = origin.ToString() + "_copy";
         if (origin.FCapabilities != null) FCapabilities = new Dictionary<string, string>(origin.FCapabilities);
      }

      public override string ToString()
      {
         return string.IsNullOrEmpty(Name) ? Service.ServiceName : Name;
      }

      public string Name
      {
         get
         {
            return FName == null ? "" : FName;
         }
         set
         {
            if (ReadOnly) return;

            FName = value;
         }
      }
      public string Host
      {
         get
         {
            return FHost;
         }
         set
         {
            if (ReadOnly) return;

            FHost = value;
         }
      }
      public ushort Port
      {
         get
         {
            return FPort;
         }
         set
         {
            if (ReadOnly) return;

            FPort = value;
         }
      }
      public bool ReadOnly
      {
         get
         {
            return FReadOnly;
         }
      }
      public bool ViaGateway
      {
         get
         {
            return FViaGateway;
         }
         set
         {
            if (ReadOnly) return;

            FViaGateway = value;
         }
      }
      public bool UserDefined
      {
         get
         {
            return FUserDefined;
         }
      }
      public ServiceInfo Service
      {
         get
         {
            return FService;
         }
         set
         {
            if (ReadOnly || FService == value) return;

            FService.RemoveConfig(this);
            FService = value;
         }
      }
      public string TransportString
      {
         get
         {
            return FService.ServiceName + ";" + FService.Driver.AssemblyName + ";" + Name;
         }
      }
      public virtual XElement XObject
      {
         get
         {
            XElement x = new XElement("Configuration");

            if (!string.IsNullOrEmpty(FName)) x.SetAttributeValue("Name", FName);
            if (!string.IsNullOrEmpty(FHost)) x.SetAttributeValue("Host", FHost);
            if (FPort > 0) x.SetAttributeValue("Port", FPort);
            if (ReadOnly) x.SetAttributeValue("ReadOnly", ReadOnly);
            if (FViaGateway) x.SetAttributeValue("ViaGateway", FViaGateway);
            if (!FUserDefined) x.SetAttributeValue("UserDefined", FUserDefined);

            if (FCapabilities != null && FCapabilities.Count > 0)
            {
               XElement supports = new XElement("Supports");

               if (FCapabilities.ContainsKey("Features")) supports.SetAttributeValue("Features", FCapabilities["Features"]);
               if (FCapabilities.ContainsKey("Actions")) supports.SetAttributeValue("Actions", FCapabilities["Actions"]);
               if (FCapabilities.ContainsKey("SearchBy")) supports.SetAttributeValue("SearchBy", FCapabilities["SearchBy"]);
               if (FCapabilities.ContainsKey("GettingOf")) supports.SetAttributeValue("GettingOf", FCapabilities["GettingOf"]);
               if (FCapabilities.ContainsKey("SettingOf")) supports.SetAttributeValue("SettingOf", FCapabilities["SettingOf"]);

               x.Add(supports);
            }

            return x;
         }
         set
         {
            if (value == null) return;

            XAttribute name = value.Attribute("Name");
            XAttribute host = value.Attribute("Host");
            XAttribute port = value.Attribute("Port");
            XAttribute readOnly = value.Attribute("ReadOnly");
            XAttribute viaGateway = value.Attribute("ViaGateway");
            XAttribute userDefined = value.Attribute("UserDefined");

            if (name != null) FName = (string)name;
            if (host != null) FHost = (string)host;
            if (port != null) FPort = (ushort)(int)port;
            if (readOnly != null) FReadOnly = (bool)readOnly;
            if (viaGateway != null) FViaGateway = (bool)viaGateway;
            if (userDefined != null) FUserDefined = (bool)userDefined;

            if (FCapabilities != null) FCapabilities.Clear();

            XElement supports = value.Element("Supports");

            if (supports != null)
            {
               if (FCapabilities == null) FCapabilities = new Dictionary<string, string>();

               XAttribute xFeatures = supports.Attribute("Features");
               XAttribute xActions = supports.Attribute("Actions");
               XAttribute xSearchBy = supports.Attribute("SearchBy");
               XAttribute xGettingOf = supports.Attribute("GettingOf");
               XAttribute xSettingOf = supports.Attribute("SettingOf");

               if (xFeatures != null) FCapabilities.Add("Features", xFeatures.Value);
               if (xActions != null) FCapabilities.Add("Actions", xActions.Value);
               if (xSearchBy != null) FCapabilities.Add("SearchBy", xSearchBy.Value);
               if (xGettingOf != null) FCapabilities.Add("GettingOf", xGettingOf.Value);
               if (xSettingOf != null) FCapabilities.Add("SettingOf", xSettingOf.Value);
            }
            else
            {
               FCapabilities = null;
            }
         }
      }
      public virtual bool AuthCurrentUser
      {
         get
         {
            return false;
         }
      }

      public virtual bool SupportsAction(ServiceActions action)
      {
         if (FCapabilities == null || !FCapabilities.ContainsKey("Actions")) return true;

         return FCapabilities["Actions"].ToLower().Contains(action.ToString().ToLower());
      }
      public virtual bool SupportsSearchBy(ServiceFields field)
      {
         if (FCapabilities == null || !FCapabilities.ContainsKey("SearchBy")) return true;

         return FCapabilities["SearchBy"].ToLower().Contains(field.ToString().ToLower());
      }
      public virtual bool SupportsGettingOf(ServiceFields field)
      {
         if (FCapabilities == null || !FCapabilities.ContainsKey("GettingOf")) return true;

         return FCapabilities["GettingOf"].ToLower().Contains(field.ToString().ToLower());
      }
      public virtual bool SupportsSettingOf(ServiceFields field)
      {
         if (FCapabilities == null || !FCapabilities.ContainsKey("SettingOf")) return true;

         return FCapabilities["SettingOf"].ToLower().Contains(field.ToString().ToLower());
      }
      public virtual bool SupportsFeature(ServiceFeatures feature)
      {
         if (FCapabilities == null || !FCapabilities.ContainsKey("Features")) return true;

         return FCapabilities["Features"].ToLower().Contains(feature.ToString().ToLower());
      }
      public virtual void AddCapabilityValue(string cap, string val)
      {
         if (FCapabilities.ContainsKey(cap) && !cap.Contains(val))
         {
            FCapabilities[cap] = FCapabilities[cap].Insert(0, val + " ");
         }
      }
      public virtual void RemoveCapabilityValue(string cap, string val)
      {
         if (FCapabilities.ContainsKey(cap))
         {
            if (string.IsNullOrEmpty(val))
            {
               FCapabilities[cap] = "";
            }
            else
            {
               FCapabilities[cap].Replace(val, "");
               FCapabilities[cap].Replace("  ", " ");
               FCapabilities[cap] = FCapabilities[cap].TrimIgnoreNull();
            }
         }
      }
   }

   #region [Serializable]

   [Serializable]
   //[KnownType(typeof(User))]
   //[KnownType(typeof(User[]))]
   //[KnownType(typeof(Status))]
   //[KnownType(typeof(XElement))]
   //[KnownType(typeof(IPEndPoint))]
   //[KnownType(typeof(ChatMessage))]
   //[KnownType(typeof(List<string>))]
   //[KnownType(typeof(ServiceCodes))]
   //[KnownType(typeof(SessionInvitation))]
   //[KnownType(typeof(SessionNotification))]
   //[KnownType(typeof(PresenceNotification))]

   #endregion
   public class ServiceMessage : ISerializable
   {
      #region ServiceMessage Data

      static int FNextId;

      protected int FSyncId;
      protected int FLocalId;
      protected int FOriginalId;
      protected int FOperationId;

      protected int FResult;
      protected string FGsid;
      protected int FAckCount;
      protected bool FAddResult;
      protected string FInformation;

      protected IntPtr FTag;
      protected User FSender;
      protected User FRecipient;
      protected ServiceActions FAction;
      protected ServiceInfo FServiceInfo;
      protected ContentType FContentType;
      protected List<string> FRecipients;
      protected IMediaSession FMediaSession;
      protected List<string> FHiddenRecipients;

      #endregion

      #region ServiceMessage Initializers

      void Initialize()
      {
         FLocalId = FNextId++;
         FOriginalId = -1;
         FOperationId = 0;
         FAction = ServiceActions.NONE;
         FRecipients = new List<string>();
         FHiddenRecipients = new List<string>();

         TsLocal = null;
         TsRemote = null;
         TsServerIn = null;
         TsServerOut = null;
      }

      [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
      public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
      {
         info.AddValue("Action", FAction.ToString());
         if (FSender != null) info.AddValue("Sender", FSender.ToString());
         if (FRecipient != null) info.AddValue("Recipient", FRecipient.ToString());
         if (FRecipients != null && FRecipients.Count > 0) info.AddValue("Recipients", FRecipients);
         if (FHiddenRecipients != null && FHiddenRecipients.Count > 0) info.AddValue("HiddenRecipients", FHiddenRecipients);
         if (!string.IsNullOrEmpty(FInformation)) info.AddValue("Information", FInformation);
         if (FOperationId != 0) info.AddValue("OperationId", FOperationId);
         if (!string.IsNullOrEmpty(FGsid)) info.AddValue("Gsid", FGsid);
         if (FOriginalId >= 0) info.AddValue("Id", FOriginalId);
         if (FAddResult) info.AddValue("Result", FResult);
         if (TsLocal != null) info.AddValue("UserTime", TsLocal.Value.ToUniversalTime().ToString("O"));
         if (TsServerIn != null) info.AddValue("ServerInTime", TsServerIn.Value.ToUniversalTime().ToString("O"));
         if (TsServerOut != null) info.AddValue("ServerOutTime", TsServerOut.Value.ToUniversalTime().ToString("O"));
      }

      public ServiceMessage()
      {
         Initialize();
      }
      public ServiceMessage(XElement xMessage)
      {
         XObject = xMessage;
      }
      public ServiceMessage(IMediaSession session)
      {
         Initialize();

         FMediaSession = session;
         FSender = session.Account;
         IList<User> recipients = session.Recipients;
         Recipients = recipients.Select(user => user.ToString());
         Recipient = recipients.Count > 0 ? recipients[0] : FSender;

         if (session.IsPersonal == false) FGsid = session.Gsid;
      }
      public ServiceMessage(User sender, IEnumerable<string> recipients)
      {
         Initialize();

         Sender = sender;
         Recipients = recipients;
      }
      protected ServiceMessage(SerializationInfo info, StreamingContext context)
      {
         FLocalId = -1;
         SerializationInfoEnumerator values = info.GetEnumerator();
         while (values.MoveNext())
         {
            if (values.Name == "Gsid") FGsid = values.Value.ToString();
            else if (values.Name == "Id") FOriginalId = (int)values.Value;
            else if (values.Name == "OperationId") FOperationId = (int)values.Value;
            else if (values.Name == "Information") FInformation = values.Value.ToString();
            else if (values.Name == "Sender") FSender = new User(values.Value.ToString());
            else if (values.Name == "Recipient") FRecipient = new User(values.Value.ToString());
            else if (values.Name == "Recipients") FRecipients = (List<string>)info.GetValue("Recipients", typeof(List<string>));
            else if (values.Name == "HiddenRecipients") FHiddenRecipients = (List<string>)info.GetValue("HiddenRecipients", typeof(List<string>));
            else if (values.Name == "Result") { FResult = (int)values.Value; FAddResult = true; }
            else if (values.Name == "Action") FAction = (ServiceActions)Enum.Parse(typeof(ServiceActions), (string)values.Value);
            else if (values.Name == "UserTime") TsRemote = (DateTime?)DateTime.Parse(values.Value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
            else if (values.Name == "ServerInTime") TsServerIn = (DateTime?)DateTime.Parse(values.Value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
            else if (values.Name == "ServerOutTime") TsServerOut = (DateTime?)DateTime.Parse(values.Value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
         }
         if (FRecipients == null) FRecipients = new List<string>();
         if (FHiddenRecipients == null) FHiddenRecipients = new List<string>();
      }
      public ServiceMessage(ServiceActions action, ServiceInfo serviceInfo, int operationId, User sender, User recipient, int result, string information)
      {
         Initialize();

         FAction = action;

         Sender = sender;
         Result = result;
         Recipient = recipient;
         Information = information;
         FServiceInfo = serviceInfo;
         FOperationId = operationId;
      }

      public static ServiceMessage Create(XElement xMessage)
      {
         XAttribute xAction = xMessage.Attribute("Action");
         ServiceActions action = xAction == null ? ServiceActions.NONE : (ServiceActions)Enum.Parse(typeof(ServiceActions), (string)xAction);

         switch (action)
         {
            case ServiceActions.TXTMSG:
            {
               return new ChatMessage(xMessage);
            }
            case ServiceActions.STATUS:
            {
               return new PresenceNotification(xMessage);
            }
            case ServiceActions.SESSIONREQ:
            {
               return new SessionInvitation(xMessage);
            }
            default:
            {
               return new ServiceMessage(xMessage);
            }
         }
      }

      #endregion

      #region ServiceMessage Properties

      public int SyncId
      {
         get
         {
            return FSyncId;
         }
         set
         {
            FSyncId = value;
         }
      }
      public int LocalId
      {
         get
         {
            return FLocalId;
         }
         set
         {
            if (FOriginalId < 0) FOriginalId = value;

            if (FLocalId < 0) FLocalId = value;
            else throw new Exception("Message Id can be assigned only once.");
         }
      }
      public int OriginalId
      {
         get
         {
            return FOriginalId;
         }
      }
      public int OperationId
      {
         get
         {
            return FOperationId;
         }
         set
         {
            FOperationId = value;
         }
      }

      public IntPtr Tag
      {
         get
         {
            return FTag;
         }
         set
         {
            FTag = value;
         }
      }
      public int Result
      {
         get
         {
            return FResult;
         }
         set
         {
            FResult = value;
            FAddResult = true;
         }
      }
      public string Gsid
      {
         get
         {
            return FGsid;
         }
         set
         {
            FGsid = value;
         }
      }
      public int AckCount
      {
         get
         {
            return FAckCount;
         }
         set
         {
            if (FAckCount < 0) FAckCount = 0;

            FAckCount = value;
         }
      }
      public bool IsIncoming
      {
         get
         {
            return FSender is Contact;
         }
      }
      public bool IsOutgoing
      {
         get
         {
            return FSender is Account;
         }
      }
      public string Information
      {
         get
         {
            return FInformation;
         }
         set
         {
            FInformation = value;
         }
      }
      public bool NoAcknowledges
      {
         get
         {
            return FAckCount < 0;
         }
         set
         {
            FAckCount = -1;
         }
      }
      public bool IsFullyAcknowledged
      {
         get
         {
            return FAckCount == FRecipients.Count + 2;
         }
      }

      public virtual string Text
      {
         get
         {
            return FInformation;
         }
         set
         {
            FInformation = value;
         }
      }
      public ContentType ContentType
      {
         get
         {
            return FContentType;
         }
      }
      public virtual XElement XObject
      {
         get
         {
            XElement xMsg = new XElement("Message");

            xMsg.SetAttributeValue("Gsid", Gsid);
            xMsg.SetAttributeValue("Action", Action.ToString());
            xMsg.SetAttributeValue("Sender", Sender.ToString());
            //if (Recipient != null) xMsg.SetAttributeValue("Recipient", Recipient.ToString());
            xMsg.SetAttributeValue("Recipients", StringExt.Join(Recipients, ", "));
            if (TsLocal.HasValue) xMsg.SetAttributeValue("LocalTime", TsLocal.Value.ToUniversalTime().ToString("O"));
            if (TsRemote.HasValue) xMsg.SetAttributeValue("UserTime", TsRemote.Value.ToUniversalTime().ToString("O"));
            if (TsServerIn.HasValue) xMsg.SetAttributeValue("ServerInTime", TsServerIn.Value.ToUniversalTime().ToString("O"));
            if (TsServerOut.HasValue) xMsg.SetAttributeValue("ServerOutTime", TsServerOut.Value.ToUniversalTime().ToString("O"));
            xMsg.SetAttributeValue("Result", FResult.ToString());
            xMsg.SetAttributeValue("Information", Information);

            return xMsg;
         }
         internal protected set
         {
            if (value == null) return;

            Initialize();

            XAttribute gsid = value.Attribute("Gsid");
            XAttribute action = value.Attribute("Action");
            XAttribute result = value.Attribute("Result");
            XAttribute sender = value.Attribute("Sender");
            XAttribute recipients = value.Attribute("Recipients");
            XAttribute information = value.Attribute("Information");

            XAttribute tsLocal = value.Attribute("LocalTime");
            XAttribute tsRemote = value.Attribute("UserTime");
            XAttribute tsServerIn = value.Attribute("ServerInTime");
            XAttribute tsServerOut = value.Attribute("ServerOutTime");

            if (gsid != null) FGsid = (string)gsid;
            if (result != null) FResult = (int)result;
            if (sender != null) FSender = new User((string)sender);
            if (information != null) FInformation = (string)information;
            if (action != null) FAction = (ServiceActions)Enum.Parse(typeof(ServiceActions), (string)action);
            if (recipients != null)
            {
               FRecipients = recipients.Value.Split(',', ';').ToList();
               if (FRecipients.Count > 0) FRecipient = new User(FRecipients[0]);
            }

            if (tsLocal != null) TsLocal = (DateTime?)tsLocal;
            if (tsRemote != null) TsRemote = (DateTime?)tsRemote;
            if (tsServerIn != null) TsServerIn = (DateTime?)tsServerIn;
            if (tsServerOut != null) TsServerOut = (DateTime?)tsServerOut;

            //if (tsLocal != null) TsLocal = (DateTime?)(DateTime.Parse(tsLocal.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
            //if (tsRemote != null) TsRemote = (DateTime?)(DateTime.Parse(tsRemote.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
            //if (tsServerIn != null) TsServerIn = (DateTime?)(DateTime.Parse(tsServerIn.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
            //if (tsServerOut != null) TsServerOut = (DateTime?)(DateTime.Parse(tsServerOut.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
         }
      }
      public IMediaSession MediaSession
      {
         get
         {
            return FMediaSession;
         }
         set
         {
            if (FMediaSession == value) return;

            if (FMediaSession != null && value != null) throw new Exception("MediaSession property is already set. It cannot be set more than one time.");

            FMediaSession = value;
         }
      }

      public User Sender
      {
         get
         {
            return FSender;
         }
         set
         {
            if (FSender != null &&
                FSender != value &&
                FSender is Contact &&
               (FSender as Contact).Owner != null &&
               (FSender as Contact).Owner.IsContact(FSender)) throw new Exception("The sender of the message cannot be changed.");

            FSender = value;
         }
      }
      public User Recipient
      {
         get
         {
            return FRecipient;
         }
         set
         {
            FRecipient = value;

            if (value != null)
            {
               string user = value.ToString();
               string badUser = new User(value.Login).ToString();

               if (FRecipients.Contains(badUser)) FRecipients.Remove(badUser);
               if (!FRecipients.Contains(user)) FRecipients.Add(user);
            }
         }
      }
      public string Header
      {
         get
         {
            return Sender.CustomName + " " +
               (TsLocal.HasValue ? TsLocal.Value.ToLocalTime().ToString("F") : "") +
               (TsServerIn.HasValue ? " [" + TsServerIn.Value.ToLocalTime().ToString("F") + "]" : "");
         }
      }
      public ServiceActions Action
      {
         get
         {
            return FAction;
         }
         set
         {
            FAction = value;
         }
      }
      public ServiceInfo ServiceInfo
      {
         get
         {
            return FServiceInfo;
         }
         set
         {
            FServiceInfo = value;
         }
      }
      public IEnumerable<string> Recipients
      {
         get
         {
            return FRecipients.ToArray();
         }
         set
         {
            FRecipients.Clear();

            foreach (string user in value) FRecipients.Add(user);
         }
      }
      public IEnumerable<string> HiddenRecipients
      {
         get
         {
            return FHiddenRecipients.ToArray();
         }
         set
         {
            FHiddenRecipients.Clear();

            foreach (string user in value) FHiddenRecipients.Add(user);
         }
      }

      public DateTime? TsLocal { get; set; }
      public DateTime? TsRemote { get; set; }
      public DateTime? TsServerIn { get; set; }
      public DateTime? TsServerOut { get; set; }

      public DateTime TsActual
      {
         get
         {
            return TsServerIn.HasValue ? TsServerIn.Value.ToLocalTime() : (TsLocal.HasValue ? TsLocal.Value.ToLocalTime() : DateTime.MinValue);
         }
      }

      #endregion

      #region ServiceMessage Members

      internal void ClearRecipients()
      {
         FRecipient = null;
         FRecipients.Clear();
         FHiddenRecipients.Clear();
      }
      public override string ToString()
      {
         return Header;
      }

      #endregion
   }

   [Serializable]
   public class ChatMessage : ServiceMessage
   {
      #region ChatMessage Data

      private bool FIsRead;
      private string FText;
      private string FSubject;
      Collection<string> FLinks;
      internal readonly bool IsSystem;

      #endregion

      #region ChatMessage Initializers

      public ChatMessage()
      {
         FAction = ServiceActions.TXTMSG;
      }
      public ChatMessage(XElement xMessage)
      {
         XObject = xMessage;
      }
      public ChatMessage(string text, IMediaSession session)
         : base(session)
      {
         Text = text;
         FAction = ServiceActions.TXTMSG;
      }
      public ChatMessage(string text, User sender, User recipient, ServiceInfo serviceInfo)
         : base(ServiceActions.TXTMSG, serviceInfo, 0, sender, recipient, 0, null)
      {
         Text = text;
      }
      public ChatMessage(ServiceActions action, string text, User sender, User recipient, ServiceInfo serviceInfo, bool isSystem)
         : base(action, serviceInfo, 0, sender, recipient, 0, null)
      {
         Text = text;
         IsSystem = isSystem;
      }
      protected ChatMessage(SerializationInfo info, StreamingContext context)
         : base(info, context)
      {
         SerializationInfoEnumerator values = info.GetEnumerator();

         while (values.MoveNext())
         {
            if (values.Name == "Text") Text = values.Value.ToString();
            else if (values.Name == "Subject") FSubject = values.Value.ToString();
            else if (values.Name == "Status") Status = (Status?)info.GetValue("Status", typeof(Status?));
         }
      }

      [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
      public override void GetObjectData(SerializationInfo info, StreamingContext context)
      {
         base.GetObjectData(info, context);

         info.AddValue("Text", Text);
         if (Status != null) info.AddValue("Status", Status);
         if (FSubject != null) info.AddValue("Subject", FSubject);
      }

      #endregion

      #region ChatMessage Properties

      public bool IsRead
      {
         get
         {
            return FIsRead;
         }
      }
      public string Subject
      {
         get
         {
            return FSubject;
         }
         set
         {
            FSubject = value;
         }
      }
      public string PlainText
      {
         get
         {
            return string.IsNullOrEmpty(FText) ? "" : FText;
         }
      }
      public override string Text
      {
         get
         {
            return FText;
         }
         set
         {
            FText = value == null ? "" : TextSvc.RemoveHtml(value);

            if (FLinks != null)
            {
               FLinks.Clear();
               FLinks = null;
            }
         }
      }
      public Nullable<Status> Status
      {
         get;
         set;
      }
      public Collection<string> Links
      {
         get
         {
            if (FLinks == null) FLinks = Utils.ExtractLinks(Text);

            return FLinks;
         }
      }
      public override XElement XObject
      {
         get
         {
            XElement xContent = new XElement("Content");

            xContent.SetAttributeValue("Type", FContentType);
            xContent.SetAttributeValue("Subject", Subject);
            xContent.SetValue(PlainText);

            XElement xMsg = base.XObject;

            xMsg.Add(xContent);

            return xMsg;
         }
         internal protected set
         {
            base.XObject = value;

            XElement xContent = value.Element("Content");

            if (xContent != null)
            {
               XAttribute type = xContent.Attribute("Type");

               FContentType = type == null ? ContentType.Text : (ContentType)Enum.Parse(typeof(ContentType), (string)type, true);

               if (FContentType == ContentType.Text || FContentType == ContentType.RichText) Text = xContent.Value;
            }
         }
      }

      #endregion

      #region ChatMessage Methods

      public override string ToString()
      {
         return Header + " - " + PlainText + "\r\n";
      }

      #endregion
   }

   [Serializable]
   public class TypingNotification : ChatMessage
   {
      public bool Erased { get; set; }

      public bool Paused { get; set; }

      public TypingNotification(bool erased, bool paused, User sender, User recipient, ServiceInfo serviceInfo)
         : base(ServiceActions.MSGTYPING, "", sender, recipient, serviceInfo, false)
      {
         Erased = erased;
         Paused = paused;
      }
   }

   [Serializable]
   public class SessionInvitation : ServiceMessage
   {
      #region SessionInvitation Data

      int FReplyCount;

      #endregion

      #region SessionInvitation Initializers

      public SessionInvitation(XElement xMessage)
      {
         XObject = xMessage;
      }
      public SessionInvitation(IMediaSession session)
         : base(session)
      {
         SessionData = session.XObject;
         FAction = ServiceActions.SESSIONREQ;
         Recipients = session.Invitees.Select(user => user.ToString());
      }
      protected SessionInvitation(SerializationInfo info, StreamingContext context)
         : base(info, context)
      {
         SessionData = (XElement)info.GetValue("SessionData", typeof(XElement));
      }

      [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
      public override void GetObjectData(SerializationInfo info, StreamingContext context)
      {
         base.GetObjectData(info, context);

         if (SessionData != null) info.AddValue("SessionData", SessionData, typeof(XElement));
      }

      #endregion

      #region SessionInvitation Properties

      public int ReplyCount
      {
         get
         {
            return FReplyCount;
         }
         set
         {
            FReplyCount = value;
         }
      }
      public bool IsExpired
      {
         get
         {
            return MediaSession == null || SessionData == null;
         }
      }
      public string SessionGsid
      {
         get
         {
            return (string)SessionData.Attribute("Gsid");
         }
      }
      public string SessionType
      {
         get
         {
            return (string)SessionData.Attribute("Type");
         }
      }
      public XElement SessionData
      {
         get;
         set;
      }
      public override string Text
      {
         get
         {
            string text = "";

            if (IsExpired)
            {
               text = "Invitation was expired.";
            }
            else
            {
               text = SessionType.SubstrBefore("Session") + " " +
                      SessionSubtype.ToString().ToLower() + " invitation. " +
                      "Invited persons: " + StringExt.Join(Recipients, ", ");
            }

            return text;
         }
      }
      public IEnumerable<User> Invitees
      {
         get
         {
            return SessionData.Element("Invitees").Elements().Select(x => new User(x.Value));
         }
      }
      public IEnumerable<User> Participants
      {
         get
         {
            return SessionData.Element("Participants").Elements().Select(x => new User(x.Value));
         }
      }
      public SessionSubtype SessionSubtype
      {
         get
         {
            return SessionData.Attributes().Count(attr => attr.Name == "Subtype") > 0 ?
               (SessionSubtype)Enum.Parse(typeof(SessionSubtype), (string)SessionData.Attribute("Subtype")) :
                SessionSubtype.Conference;
         }
      }
      public ServiceSession HostingSession
      {
         get
         {
            //return SessionData.Elements().Count(el => el.Name == "{CoreTypes}ServiceSession") > 0 ?
            //   (ServiceSession)ObjectExtension.ParseXml(typeof(ServiceSession), SessionData.Element("{CoreTypes}ServiceSession").ToString()) :
            //    null;
            throw new NotImplementedException("HostingSession");
         }
      }
      public SessionVisibility SessionVisibility
      {
         get
         {
            return SessionData.Attributes().Count(attr => attr.Name == "Visibility") > 0 ?
               (SessionVisibility)Enum.Parse(typeof(SessionVisibility), (string)SessionData.Attribute("Visibility")) :
                SessionVisibility.Private;
         }
      }

      public override XElement XObject
      {
         get
         {
            return base.XObject;
         }
         internal protected set
         {
            base.XObject = value;
         }
      }

      #endregion

      #region SessionInvitation Methods

      public override string ToString()
      {
         return base.ToString() + Text + "\r\n";
      }

      #endregion
   }

   [Serializable]
   public class PresenceNotification : ServiceMessage
   {
      #region SessionInvitation Initializers

      public PresenceNotification(XElement xMessage)
      {
         XObject = xMessage;
      }
      public PresenceNotification(StatusCodes status, string message, User sender, User recipient, ServiceInfo serviceInfo)
         : base(ServiceActions.STATUS, serviceInfo, 0, sender, recipient, 0, "")
      {
         Status = new Status(status, message);
      }
      public PresenceNotification(Status status, IPEndPoint endPoint, User sender, IEnumerable<string> recipients)
         : base(sender, recipients)
      {
         Status = status;
         EndPoint = endPoint;
         Action = ServiceActions.STATUS;
      }
      protected PresenceNotification(SerializationInfo info, StreamingContext context)
         : base(info, context)
      {
         Status = (Status)info.GetValue("Status", typeof(Status));
         EndPoint = (IPEndPoint)info.GetValue("EndPoint", typeof(IPEndPoint));
      }

      [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
      public override void GetObjectData(SerializationInfo info, StreamingContext context)
      {
         base.GetObjectData(info, context);

         info.AddValue("Status", Status, typeof(Status));
         info.AddValue("EndPoint", EndPoint, typeof(IPEndPoint));
      }

      #endregion

      #region PresenceNotification Properties

      public Status Status { get; set; }
      public Status? OldStatus { get; set; }
      public IPEndPoint EndPoint { get; set; }

      public override string Text
      {
         get
         {
            return OldStatus == null ? "User status: " + Status.ToLongString() :
                  "User changed status: " + OldStatus.Value.ToLongString() + " -> " + Status.ToLongString();
         }
      }
      public override XElement XObject
      {
         get
         {
            XElement xMsg = base.XObject;

            xMsg.SetAttributeValue("Status", Status.Value);
            if (EndPoint != null) xMsg.SetAttributeValue("EndPoint", EndPoint.ToString());

            return xMsg;
         }
         internal protected set
         {
            base.XObject = value;

            string status = (string)value.Attribute("Status");
            string endPoint = (string)value.Attribute("EndPoint");

            if (status != null) Status = new Status((StatusCodes)Enum.Parse(typeof(StatusCodes), status));
            if (endPoint != null) EndPoint = new IPEndPoint(IPAddress.Parse(endPoint.SubstrBefore(":")),
                                                            Convert.ToInt32(endPoint.SubstrAfter(":")));
         }
      }

      #endregion

      #region PresenceNotification Methods

      public override string ToString()
      {
         return base.ToString() + " " + Text + "\r\n";
      }

      #endregion
   }

   public class SystemEvent
   {
      public string Title { get; set; }
      public string Message { get; set; }
      public string StackTrace { get; set; }
      public string DateTime { get; set; }

      public SystemEvent(XElement xEvent)
      {
         Title = xEvent.Name;
         DateTime = (string)xEvent.Attribute("Time");
         Message = (string)xEvent.Attribute("Message");
         XElement xStack = xEvent.Element("Stack");
         if (xStack != null) StackTrace = xStack.Value;
      }

      public SystemEvent(string title, string timestamp, string message, string stackTrace)
      {
         Title = title;
         DateTime = timestamp;
         Message = message;
         StackTrace = stackTrace;
      }

      public override string ToString()
      {
         string xml = "<" + Title;
         xml += " Time=\"" + DateTime + "\"";
         xml += " Message=\"" + System.Security.SecurityElement.Escape(Message) + "\"";
         if (string.IsNullOrEmpty(StackTrace))
         {
            xml += " />";
         }
         else
         {
            xml += ">\r\n  <Stack>" + System.Security.SecurityElement.Escape(StackTrace) + "\r\n  </Stack>";
            xml += "</" + Title + ">";
         }
         return xml;
      }
   }

   public class NetworkOperation
   {
      int FId;
      int FTimeout;
      string FName;
      Account FAccount;
      int FAttemptCount;
      DateTime FStartTime;
      OperationState FState;
      ServiceEntrance FEntrance;
      ServiceSession FServiceSession;
      Dictionary<string, object> FParams;
      Dictionary<string, object> FResults;

      static int FNextId;

      public int Id
      {
         get
         {
            return FId;
         }
      }
      public int Timeout
      {
         get
         {
            return FTimeout;
         }
         set
         {
            FTimeout = value;
            FStartTime = DateTime.Now;
         }
      }
      public string Name
      {
         get
         {
            return FName;
         }
      }
      public Account Account
      {
         get
         {
            return FAccount;
         }
         set
         {
            FAccount = value;
         }
      }
      public int AttemptCount
      {
         get
         {
            return FAttemptCount;
         }
         set
         {
            FAttemptCount = value;
         }
      }
      public DateTime StartTime
      {
         get
         {
            return FStartTime;
         }
      }
      public bool KeepConnection
      {
         get;
         set;
      }
      public OperationState State
      {
         get
         {
            return FState;
         }
         set
         {
            FState = value;
         }
      }
      public ServiceEntrance Entrance
      {
         get
         {
            return FEntrance;
         }
         set
         {
            FEntrance = value;
         }
      }
      public ServiceSession ServiceSession
      {
         get
         {
            return FServiceSession;
         }
         set
         {
            FServiceSession = value;
         }
      }
      public Dictionary<string, object> Params
      {
         get
         {
            return FParams;
         }
      }
      public Dictionary<string, object> Results
      {
         get
         {
            return FResults;
         }
      }
      public NetworkOperationDelegate NetworkOperationCallback { get; set; }
      public MessagingOperationDelegate MessagingOperationCallback { get; set; }

      static int GetNextId()
      {
         return ++FNextId;
      }

      public NetworkOperation(string name, int timeout, ServiceSession session, params object[] args)
      {
         FName = name;
         FId = GetNextId();
         FTimeout = timeout;
         KeepConnection = true;
         FStartTime = DateTime.Now;
         FServiceSession = session;
         FParams = new Dictionary<string, object>();
         FResults = new Dictionary<string, object>();

         for (int i = 0; i < (int)(args.Length / 2); i++)
         {
            FParams[args[i * 2].ToString()] = args[i * 2 + 1];
         }
      }
      public NetworkOperation(int id, string name, int timeout, ServiceSession session, params object[] args)
      {
         FId = id;
         FName = name;
         FTimeout = timeout;
         KeepConnection = true;
         FStartTime = DateTime.Now;
         FServiceSession = session;
         FParams = new Dictionary<string, object>();
         FResults = new Dictionary<string, object>();

         for (int i = 0; i < (int)(args.Length / 2); i++)
         {
            FParams[args[i * 2].ToString()] = args[i * 2 + 1];
         }
      }

      public override string ToString()
      {
         return Name;
      }
   }

   public class MediaSessionTypeInfo
   {
      public readonly Image Image;
      public readonly string Caption;
      public readonly Type SessionType;
      public readonly Type VisualizerType;

      public MediaSessionTypeInfo(Type sessionType, Type visualizerType, string caption, Image image)
      {
         Image = image;
         Caption = caption;
         SessionType = sessionType;
         VisualizerType = visualizerType;
      }
   }


   public abstract class CommunicationEventHanldlerObject : ICommunicationEventObject, ICommunicationEventHanldlerObject
   {
      bool FLogEvents = false;
      Collection<ServiceMessage> FEvents = new Collection<ServiceMessage>();

      public bool LogEvents
      {
         get
         {
            return FLogEvents;
         }
         set
         {
            FLogEvents = value;
         }
      }
      public Collection<ServiceMessage> Events
      {
         get
         {
            return FEvents;
         }
      }

      public event EventHandler<ErrorEventArgs> UnhandledError;
      public event EventHandler<ErrorEventArgs> InvalidArgumentError;
      public event EventHandler<ErrorEventArgs> ServiceOperationError;
      public event EventHandler<MessagingOperationEventArgs> InvalidMessageEvent;

      public event EventHandler<NetworkOperationEventArgs> LogonState;
      public event EventHandler<NetworkOperationEventArgs> SeekUsersState;
      public event EventHandler<NetworkOperationEventArgs> DropAccountState;
      public event EventHandler<NetworkOperationEventArgs> SyncContactsState;
      public event EventHandler<NetworkOperationEventArgs> CreateAccountState;
      public event EventHandler<NetworkOperationEventArgs> NotifyPresenceState;
      public event EventHandler<NetworkOperationEventArgs> RequestUserInfoState;
      public event EventHandler<SaveAccountDataEventArgs> SaveAccountDataState;
      public event EventHandler<NetworkOperationEventArgs> LoadAccountDataState;
      public event EventHandler<SaveMediaSessionEventArgs> SaveMediaSessionState;
      public event EventHandler<MessagingOperationEventArgs> SendInvitationState;
      public event EventHandler<MessagingOperationEventArgs> SendChatMessageState;
      public event EventHandler<MessagingOperationEventArgs> SendAcknowledgeState;
      public event EventHandler<MessagingOperationEventArgs> UpdateChatMessageState;
      public event EventHandler<MessagingOperationEventArgs> SendInvitationReplyState;

      public event EventHandler<MessagingOperationEventArgs> PresenceNotified;
      public event EventHandler<MessagingOperationEventArgs> InvitationReceived;
      public event EventHandler<MessagingOperationEventArgs> ChatMessageReceived;
      public event EventHandler<MessagingOperationEventArgs> AcknowledgeReceived;
      public event EventHandler<MediaSessionUpdateEventArgs> MediaSessionUpdated;
      public event EventHandler<MessagingOperationEventArgs> InvitationReplyReceived;
      public event EventHandler<MessagingOperationEventArgs> ChatMessageUpdateReceived;

      public void FireUnhandledErrorEvent(Exception error)
      {
         if (UnhandledError != null) UnhandledError(this, new ErrorEventArgs(error));
      }
      public void FireInvalidMessageEvent(MessagingOperationEventArgs args)
      {
         if (InvalidMessageEvent != null) InvalidMessageEvent(this, args);
      }
      public void FireServiceOperationErrorEvent(ServiceOperationException error)
      {
         if (ServiceOperationError != null) ServiceOperationError(this, new ErrorEventArgs(error));
      }

      public virtual void OnLogonState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (LogonState != null) LogonState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSeekUsersState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (SeekUsersState != null) SeekUsersState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnDropAccountState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (DropAccountState != null) DropAccountState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSyncContactsState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (SyncContactsState != null) SyncContactsState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnCreateAccountState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (CreateAccountState != null) CreateAccountState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSaveMediaSessionState(object sender, SaveMediaSessionEventArgs e)
      {
         try
         {
            if (SaveMediaSessionState != null) SaveMediaSessionState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnNotifyPresenceState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (NotifyPresenceState != null) NotifyPresenceState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnRequestUserInfoState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (RequestUserInfoState != null) RequestUserInfoState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSaveAccountDataState(object sender, SaveAccountDataEventArgs e)
      {
         try
         {
            if (SaveAccountDataState != null) SaveAccountDataState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnLoadAccountDataState(object sender, NetworkOperationEventArgs e)
      {
         try
         {
            if (LoadAccountDataState != null) LoadAccountDataState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSendChatMessageState(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents && e.State == OperationState.DataSending) Events.Add(e.RequestMessage);

            if (SendChatMessageState != null) SendChatMessageState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnUpdateChatMessageState(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents && e.State == OperationState.DataSending) Events.Add(e.EventMessage);

            if (UpdateChatMessageState != null) UpdateChatMessageState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSendInvitationState(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents && e.State == OperationState.DataSending) Events.Add(e.EventMessage);

            if (SendInvitationState != null) SendInvitationState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSendAcknowledgeState(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents && e.State == OperationState.DataSending) Events.Add(e.EventMessage);

            if (SendAcknowledgeState != null) SendAcknowledgeState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnSendInvitationReplyState(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents && e.State == OperationState.DataSending) Events.Add(e.EventMessage);

            if (SendInvitationReplyState != null) SendInvitationReplyState(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnChatMessageReceived(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            e.EventMessage.TsLocal = DateTime.UtcNow;

            if (LogEvents) Events.Add(e.EventMessage);

            if (ChatMessageReceived != null) ChatMessageReceived(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnPresenceNotified(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            e.EventMessage.TsLocal = DateTime.UtcNow;

            if (LogEvents) Events.Add(e.EventMessage);

            if (PresenceNotified != null) PresenceNotified(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnInvitationReceived(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            e.EventMessage.TsLocal = DateTime.UtcNow;

            if (LogEvents) Events.Add(e.EventMessage);

            if (InvitationReceived != null) InvitationReceived(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnAcknowledgeReceived(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents) Events.Add(e.EventMessage);

            if (AcknowledgeReceived != null) AcknowledgeReceived(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnMediaSessionUpdated(object sender, MediaSessionUpdateEventArgs e)
      {
         try
         {
            if (MediaSessionUpdated != null) MediaSessionUpdated(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnChatMessageUpdateReceived(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents) Events.Add(e.EventMessage);

            if (ChatMessageUpdateReceived != null) ChatMessageUpdateReceived(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
      public virtual void OnInvitationReplyReceived(object sender, MessagingOperationEventArgs e)
      {
         try
         {
            if (LogEvents) Events.Add(e.EventMessage);

            if (InvitationReplyReceived != null) InvitationReplyReceived(this, e);
         }
         catch (Exception error)
         {
            FireUnhandledErrorEvent(error);
         }
      }
   }

   public abstract class CommunicationServiceObjectBase : CommunicationEventHanldlerObject, ICommunicationServiceObject
   {
      #region CommunicationServiceObjectBase Data

      protected int FListenPort;
      protected int FTimeout = 20;
      protected int FLongTimeout = 40;
      protected int FLogonTimeout = 120;
      protected string FAssemblyName;
      protected ContextMenuStrip FMenu;
      protected string FDriverErrorPrefix;
      protected XElement FDefaultXSettings;
      protected List<ServiceInfo> FServices;
      protected ServiceConfig FDefaultConfig;
      protected IBaseCommunicator FCommunicator;
      protected System.Threading.Timer FTimeoutTimer;
      protected NetworkOperationSet FNetworkOperations;

      #endregion

      #region CommunicationServiceObjectBase Initializers

      public CommunicationServiceObjectBase(string assemblyName, IBaseCommunicator owner)
      {
         FCommunicator = owner;
         FAssemblyName = assemblyName;
         FServices = new List<ServiceInfo>();
         FNetworkOperations = new NetworkOperationSet();
         FDriverErrorPrefix = Resources.S_Network_driver_error;
         FTimeoutTimer = new System.Threading.Timer(OperationTimeoutTimerHandler, null, 1000, 1000);

         FMenu = new ContextMenuStrip();
         ToolStripItem homePage = new ToolStripMenuItem(Resources.S_Home_page, Resources.HomeHS, (s, e) => { GoVendorPage(VendorPage.HomePage, null); });
         homePage.Name = "homePage";
         //Image globe = Resources.Globe16;
         Image globe = ResourceWrapper.GetImage("Earth_16");
         FMenu.Items.AddRange(new ToolStripItem[] { homePage,
            new ToolStripMenuItem(Resources.S_Personal_page, globe, (s, e) => { GoVendorPage(VendorPage.PersonalInfo, null); }),
            new ToolStripMenuItem(Resources.S_Registration_page, globe, (s, e) => { GoVendorPage(VendorPage.RegisterUser, null); }),
            new ToolStripMenuItem(Resources.S_Restore_password_page, globe, (s, e) => { GoVendorPage(VendorPage.RestorePassword, null); })});
      }

      ~CommunicationServiceObjectBase()
      {
         FTimeoutTimer.Dispose();
      }

      #endregion

      #region ICommunicationServiceObject Properties

      public int ListenPort
      {
         get
         {
            return FListenPort;
         }
      }
      public ContextMenuStrip Menu
      {
         get
         {
            return FMenu;
         }
      }
      public IBaseCommunicator Communicator
      {
         get
         {
            return FCommunicator;
         }
      }

      #endregion

      #region ICommunicationServiceObject Abstract Members

      public abstract void Logoff(Account account);
      public abstract void DropAccount(Account account);
      public abstract void SyncContacts(Account account);
      public abstract void LoadAccountData(Account account);
      public abstract void CreateAccount(RegistrationInfo info);
      public abstract void SendChatMessage(ChatMessage message);
      public abstract void UpdateMessage(ServiceMessage message);
      public abstract void Logon(Account account, Status status);
      public abstract void SeekUsers(string query, IWindow sender);
      public abstract void SendAcknowledge(ServiceMessage message);
      public abstract void SendInvitation(SessionInvitation message);
      public abstract void RequestUserInfo(User user, bool shortInfo);
      public abstract void SendInvitationReply(ServiceMessage message);
      public abstract void NotifyPresence(Account account, User user, Status status);
      public abstract void ChangePrivateData(Account account, string password, string mail);
      public abstract void SaveAccountData(Account account, SaveAccountActions actions, params object[] data);
      public abstract void SaveMediaSession(IMediaSession session, SaveMediaSessionActions actions, params object[] data);
      public abstract void SetPermissions(Account account, object target, PolicyRule message, PolicyRule invitation, PolicyRule visibility);

      public abstract bool IsConnected(Account account);
      public virtual bool IsLogonInProgress(Account account)
      {
         lock (FNetworkOperations)
         {
            return FNetworkOperations.ContainsName(account.Login + ".LOGON");
         }
      }
      public abstract void ShowDriverSettings(string configName);
      public abstract ushort AddListener(string name, ushort port);
      public virtual void GoVendorPage(VendorPage page, object tag)
      {
         (FCommunicator.CurrentAccount != null ?
            FCommunicator.CurrentAccount.ServiceConfig.Service :
            DefaultConfig.Service).GoVendorPage(page);
      }
      public abstract IRegisterWidget CreateRegisterWidget(ServiceConfig config);

      #endregion

      #region CommunicationServiceObjectBase Events

      public event EventHandler SettingsChanged;

      public void FireSettingsChanged()
      {
         if (SettingsChanged != null) SettingsChanged(this, EventArgs.Empty);
      }

      #endregion

      #region CommunicationServiceObjectBase Members

      public string AssemblyName
      {
         get
         {
            if (FAssemblyName == null) FAssemblyName = "";

            return FAssemblyName;
         }
         internal set
         {
            FAssemblyName = value;
         }
      }
      public ServiceInfo[] Services
      {
         get
         {
            return FServices.ToArray();
         }
      }
      public XElement DefaultXSettings
      {
         get
         {
            if (FDefaultXSettings == null) ResetNetworkSettings();

            return FDefaultXSettings;
         }
         set
         {
            ModifyDefaultNetworkSettings(value);
         }
      }
      public virtual XElement XSettings
      {
         get
         {
            XElement xSettings = new XElement(AssemblyName);

            foreach (ServiceInfo service in FServices)
            {
               XElement xService = service.XObject;
               XElement xService2 = FDefaultXSettings == null ? null :
                                    FDefaultXSettings.Elements("Service").FirstOrDefault(x =>
                                    (string)x.Attribute("Name") == service.ServiceName);
               if (xService2 == null)
               {
                  xSettings.Add(xService);
               }
               else if (xService2.ToString() != xService.ToString())
               {
                  IEnumerable<XElement> xConfigs = xService2.Elements("Configuration");

                  xSettings.Add(service.GetXLiteObject(service.Configurations.Where(c =>
                                xConfigs.Count(x => c.Name.EqualsIgnoreNull((string)x.Attribute("Name"))) == 0)));
               }
            }
            return xSettings;
         }
         set
         {
            if (value == null) return;

            try
            {
               foreach (XElement xService in value.Elements("Service"))
               {
                  ServiceInfo service = FServices.FirstOrDefault(svc =>
                                        svc.ServiceName == (string)xService.Attribute("Name"));
                  if (service == null)
                  {
                     FServices.Add(service = new ServiceInfo(this, xService));
                  }
                  foreach (XElement xConfig in xService.Elements())
                  {
                     service.AddConfig(CreateConfig(xConfig, service));
                  }
               }
            }
            catch (Exception error)
            {
               FCommunicator.ShowError("Unable to parse network settings.", error);
            }
         }
      }
      public ServiceConfig DefaultConfig
      {
         get
         {
            if (FDefaultConfig == null) CreateDefaultConfig();

            return FDefaultConfig;
         }
      }
      public List<ServiceConfig> Configurations
      {
         get
         {
            List<ServiceConfig> configurations = new List<ServiceConfig>();
            foreach (var svc in FServices) configurations.AddRange(svc.Configurations);
            return configurations;
         }
      }
      public virtual bool SupportsLocalNetworks
      {
         get
         {
            return false;
         }
      }

      public void ResetNetworkSettings()
      {
         ModifyDefaultNetworkSettings(XElement.Parse(GetDefaultNetworkSettings().Replace('\'', '\"')));
      }
      public void AddConfig(ServiceConfig config)
      {
         config.Service.AddConfig(config);
      }
      public void RemoveConfig(ServiceConfig config)
      {
         config.Service.RemoveConfig(config);
      }
      public virtual ServiceConfig GetLocalNetworkConfig(bool getDefault)
      {
         return null;
      }
      public virtual ServiceConfig CreateConfig(XElement xConfig, ServiceInfo info)
      {
         return new ServiceConfig(xConfig, info);
      }

      protected abstract void CreateDefaultConfig();
      protected abstract string GetDefaultNetworkSettings();
      private void ModifyDefaultNetworkSettings(XElement value)
      {
         FServices.Clear();
         XSettings = value;

         FDefaultXSettings = new XElement(AssemblyName);
         foreach (ServiceInfo service in FServices) FDefaultXSettings.Add(service.XObject);
      }
      protected ServiceOperationException GetOperationFault(ErrorReasonCode reason)
      {
         return new ServiceOperationException(reason, FDriverErrorPrefix);
      }
      protected ServiceOperationException GetOperationFault(ErrorReasonCode reason, string message, string tag)
      {
         if (string.IsNullOrEmpty(message))
         {
            switch (reason)
            {
               case ErrorReasonCode.NotSupported:
               {
                  message = Resources.S_This_feature_is_not_available_on_the_server_side;
                  break;
               }
               case ErrorReasonCode.ExistAlready:
               {
                  message = Resources.S_This_operation_has_been_already_started;
                  break;
               }
               default:
               {
                  message = reason.ToString() + (string.IsNullOrEmpty(tag) ? "" : ": <tag>");
                  break;
               }
            }
         }
         return new ServiceOperationException(reason, FDriverErrorPrefix + message.Replace("<tag>", tag));
      }

      protected virtual void RemoveOperation(string name)
      {
         lock (FNetworkOperations)
         {
            NetworkOperation nwo = FNetworkOperations.GetItemByName(name);
            if (nwo != null) FNetworkOperations.Remove(nwo);
         }
      }
      protected bool IsOperationValid(NetworkOperation nwo)
      {
         lock (FNetworkOperations)
         {
            return FNetworkOperations.Contains(nwo);
         }
      }
      protected NetworkOperation GetOperationByName(string name)
      {
         lock (FNetworkOperations)
         {
            return FNetworkOperations.GetItemByName(name);
         }
      }
      protected virtual void RemoveOperation(NetworkOperation nwo)
      {
         if (nwo != null)
         {
            lock (FNetworkOperations)
            {
               FNetworkOperations.Remove(nwo);
            }
         }
      }
      protected NetworkOperation CreateOperation(string name, bool unique, Account account, int timeout, ServiceSession session, params object[] args)
      {
         NetworkOperation nwo = null;

         lock (FNetworkOperations)
         {
            if (unique && FNetworkOperations.ContainsName(name)) return null;

            nwo = new NetworkOperation(name, timeout, session, args);

            nwo.Account = account;

            FNetworkOperations.AddItem(nwo);
         }

         return nwo;
      }

      #endregion

      #region CommunicationServiceObjectBase Handlers

      private void OperationTimeoutTimerHandler(object sender)
      {
         lock (FNetworkOperations)
         {
            NetworkOperation nwo = null;

            foreach (NetworkOperation _nwo in FNetworkOperations)
            {
               if (_nwo.Timeout > 0 && DateTime.Now.Subtract(_nwo.StartTime).Seconds >= _nwo.Timeout)
               {
                  nwo = _nwo;

                  if (_nwo.NetworkOperationCallback != null) _nwo.NetworkOperationCallback(this,
                     new NetworkOperationEventArgs(_nwo, new ServiceOperationException(ErrorReasonCode.Timeout)));

                  else if (_nwo.MessagingOperationCallback != null) _nwo.MessagingOperationCallback(this,
                     new MessagingOperationEventArgs(_nwo.Params["Message"] as ServiceMessage, _nwo, OperationState.Faulted, new ServiceOperationException(ErrorReasonCode.Timeout)));

                  break;
               }
            }

            if (nwo != null) RemoveOperation(nwo);
         }
      }

      #endregion
   }
}